Skip to content

Commit 295404a

Browse files
authored
Extend diagnose command (#2637)
1 parent 371c292 commit 295404a

File tree

4 files changed

+198
-35
lines changed

4 files changed

+198
-35
lines changed

internal/diagnostics/metadata.go

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@ import (
1212
)
1313

1414
type Metadata struct {
15-
GlobalAccountID string `json:"globalAccountID" yaml:"globalAccountID"`
16-
SubaccountID string `json:"subaccountID" yaml:"subaccountID"`
17-
Provider string `json:"provider" yaml:"provider"`
18-
KubernetesVersion string `json:"kubernetesVersion" yaml:"kubernetesVersion"`
19-
NATGatewayIPs []string `json:"natGatewayIPs" yaml:"natGatewayIPs"`
20-
KubeAPIServer string `json:"kubeAPIServer" yaml:"kubeAPIServer"`
15+
GlobalAccountID string `json:"globalAccountID" yaml:"globalAccountID"`
16+
SubaccountID string `json:"subaccountID" yaml:"subaccountID"`
17+
ClusterID string `json:"clusterID" yaml:"clusterID"`
18+
ClusterDomain string `json:"clusterDomain" yaml:"clusterDomain"`
19+
Region string `json:"region" yaml:"region"`
20+
ShootName string `json:"shootName" yaml:"shootName"`
21+
Provider string `json:"provider" yaml:"provider"`
22+
KubernetesVersion string `json:"kubernetesVersion" yaml:"kubernetesVersion"`
23+
NATGatewayIPs []string `json:"natGatewayIPs" yaml:"natGatewayIPs"`
24+
GardenerExtensions []string `json:"gardenerExtensions" yaml:"gardenerExtensions"`
25+
KubeAPIServer string `json:"kubeAPIServer" yaml:"kubeAPIServer"`
2126
}
2227

2328
type MetadataCollector struct {
@@ -35,6 +40,7 @@ func NewMetadataCollector(client kube.Client, writer io.Writer, verbose bool) *M
3540
func (mc *MetadataCollector) Run(ctx context.Context) Metadata {
3641
var metadata Metadata
3742

43+
mc.enrichMetadataWithSapBtpManagerSecret(ctx, &metadata)
3844
mc.enrichMetadataWithShootInfo(ctx, &metadata)
3945
mc.enrichMetadataWithKymaInfo(ctx, &metadata)
4046
mc.enrichMetadataWithKymaProvisioningInfo(ctx, &metadata)
@@ -43,6 +49,20 @@ func (mc *MetadataCollector) Run(ctx context.Context) Metadata {
4349
return metadata
4450
}
4551

52+
func (mc *MetadataCollector) enrichMetadataWithSapBtpManagerSecret(ctx context.Context, metadata *Metadata) {
53+
secret, err := mc.client.Static().CoreV1().Secrets("kyma-system").Get(ctx, "sap-btp-manager", metav1.GetOptions{})
54+
if err != nil {
55+
mc.WriteVerboseError(err, "Failed to get sap-btp-manager secret")
56+
return
57+
}
58+
59+
if secret.Data["cluster_id"] == nil {
60+
return
61+
}
62+
63+
metadata.ClusterID = string(secret.Data["cluster_id"])
64+
}
65+
4666
func (mc *MetadataCollector) enrichMetadataWithClusterConfigInfo(ctx context.Context, metadata *Metadata) {
4767
networkProblemDetectorConfigMap, err := mc.client.Static().CoreV1().
4868
ConfigMaps("kube-system").
@@ -90,6 +110,23 @@ func (mc *MetadataCollector) enrichMetadataWithShootInfo(ctx context.Context, me
90110
if k8sVersion, exists := shootInfoConfigMap.Data["kubernetesVersion"]; exists {
91111
metadata.KubernetesVersion = k8sVersion
92112
}
113+
114+
if domain, exists := shootInfoConfigMap.Data["domain"]; exists {
115+
metadata.ClusterDomain = domain
116+
}
117+
118+
if region, exists := shootInfoConfigMap.Data["region"]; exists {
119+
metadata.Region = region
120+
}
121+
122+
if shootName, exists := shootInfoConfigMap.Data["shootName"]; exists {
123+
metadata.ShootName = shootName
124+
}
125+
126+
if extensions, exists := shootInfoConfigMap.Data["extensions"]; exists {
127+
splitExtensions := strings.Split(extensions, ",")
128+
metadata.GardenerExtensions = append(metadata.GardenerExtensions, splitExtensions...)
129+
}
93130
}
94131

95132
func (mc *MetadataCollector) enrichMetadataWithKymaInfo(ctx context.Context, metadata *Metadata) {

internal/diagnostics/metadata_test.go

Lines changed: 131 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"errors"
7+
"strings"
78
"testing"
89

910
"github.com/kyma-project/cli.v3/internal/diagnostics"
@@ -79,25 +80,35 @@ func TestWriteVerboseError(t *testing.T) {
7980
func TestEnrichMetadataWithShootInfo(t *testing.T) {
8081
// Test cases
8182
testCases := []struct {
82-
name string
83-
shootInfoExists bool
84-
provider string
85-
kubernetesVersion string
86-
verbose bool
83+
name string
84+
shootInfoExists bool
85+
shootInfoConfigMap *corev1.ConfigMap
86+
verbose bool
8787
}{
8888
{
89-
name: "Should enrich metadata when shoot-info ConfigMap exists",
90-
shootInfoExists: true,
91-
provider: "azure",
92-
kubernetesVersion: "1.26.0",
93-
verbose: false,
89+
name: "Should enrich metadata when shoot-info ConfigMap exists",
90+
shootInfoExists: true,
91+
shootInfoConfigMap: &corev1.ConfigMap{
92+
ObjectMeta: metav1.ObjectMeta{
93+
Name: "shoot-info",
94+
Namespace: "kube-system",
95+
},
96+
Data: map[string]string{
97+
"provider": "azure",
98+
"kubernetesVersion": "1.26.0",
99+
"domain": "c-513b462.sample-domain.com",
100+
"region": "westeurope",
101+
"shootName": "c-513b462",
102+
"extensions": "extension-1,extension-2,extension-3",
103+
},
104+
},
105+
verbose: false,
94106
},
95107
{
96-
name: "Should not enrich metadata when shoot-info ConfigMap does not exist",
97-
shootInfoExists: false,
98-
provider: "",
99-
kubernetesVersion: "",
100-
verbose: true,
108+
name: "Should not enrich metadata when shoot-info ConfigMap does not exist",
109+
shootInfoExists: false,
110+
shootInfoConfigMap: &corev1.ConfigMap{},
111+
verbose: true,
101112
},
102113
}
103114

@@ -112,17 +123,10 @@ func TestEnrichMetadataWithShootInfo(t *testing.T) {
112123

113124
// Create the ConfigMap if needed for the test case
114125
if tc.shootInfoExists {
115-
shootInfoCM := &corev1.ConfigMap{
116-
ObjectMeta: metav1.ObjectMeta{
117-
Name: "shoot-info",
118-
Namespace: "kube-system",
119-
},
120-
Data: map[string]string{
121-
"provider": tc.provider,
122-
"kubernetesVersion": tc.kubernetesVersion,
123-
},
124-
}
125-
_, err := fakeClient.CoreV1().ConfigMaps("kube-system").Create(context.TODO(), shootInfoCM, metav1.CreateOptions{})
126+
_, err := fakeClient.
127+
CoreV1().
128+
ConfigMaps("kube-system").
129+
Create(context.TODO(), tc.shootInfoConfigMap, metav1.CreateOptions{})
126130
assert.NoError(t, err)
127131
}
128132

@@ -133,8 +137,12 @@ func TestEnrichMetadataWithShootInfo(t *testing.T) {
133137

134138
// Then
135139
if tc.shootInfoExists {
136-
assert.Equal(t, tc.provider, metadata.Provider)
137-
assert.Equal(t, tc.kubernetesVersion, metadata.KubernetesVersion)
140+
assert.Equal(t, tc.shootInfoConfigMap.Data["provider"], metadata.Provider)
141+
assert.Equal(t, tc.shootInfoConfigMap.Data["kubernetesVersion"], metadata.KubernetesVersion)
142+
assert.Equal(t, tc.shootInfoConfigMap.Data["domain"], metadata.ClusterDomain)
143+
assert.Equal(t, tc.shootInfoConfigMap.Data["region"], metadata.Region)
144+
assert.Equal(t, tc.shootInfoConfigMap.Data["shootName"], metadata.ShootName)
145+
assert.Equal(t, tc.shootInfoConfigMap.Data["extensions"], strings.Join(metadata.GardenerExtensions, ","))
138146
assert.Empty(t, writer.String()) // No errors should be written
139147
} else {
140148
assert.Empty(t, metadata.Provider)
@@ -294,3 +302,98 @@ func TestEnrichMetadataWithKymaProvisioningInfo(t *testing.T) {
294302
})
295303
}
296304
}
305+
306+
func TestEnrichMetadataWithSapBtpManagerSecret(t *testing.T) {
307+
testCases := []struct {
308+
name string
309+
secretExists bool
310+
secret *corev1.Secret
311+
expectedClusterID string
312+
verbose bool
313+
expectedErrorOutput string
314+
}{
315+
{
316+
name: "Should enrich metadata when sap-btp-manager secret exists with cluster_id",
317+
secretExists: true,
318+
secret: &corev1.Secret{
319+
ObjectMeta: metav1.ObjectMeta{
320+
Name: "sap-btp-manager",
321+
Namespace: "kyma-system",
322+
},
323+
Data: map[string][]byte{
324+
"cluster_id": []byte("test-cluster-123"),
325+
},
326+
},
327+
expectedClusterID: "test-cluster-123",
328+
verbose: false,
329+
},
330+
{
331+
name: "Should not enrich metadata when sap-btp-manager secret exists but cluster_id is missing",
332+
secretExists: true,
333+
secret: &corev1.Secret{
334+
ObjectMeta: metav1.ObjectMeta{
335+
Name: "sap-btp-manager",
336+
Namespace: "kyma-system",
337+
},
338+
Data: map[string][]byte{
339+
"other_data": []byte("some-value"),
340+
},
341+
},
342+
expectedClusterID: "",
343+
verbose: false,
344+
},
345+
{
346+
name: "Should handle missing sap-btp-manager secret gracefully",
347+
secretExists: false,
348+
secret: nil,
349+
expectedClusterID: "",
350+
verbose: true,
351+
expectedErrorOutput: "Failed to get sap-btp-manager secret",
352+
},
353+
{
354+
name: "Should handle empty cluster_id data",
355+
secretExists: true,
356+
secret: &corev1.Secret{
357+
ObjectMeta: metav1.ObjectMeta{
358+
Name: "sap-btp-manager",
359+
Namespace: "kyma-system",
360+
},
361+
Data: map[string][]byte{
362+
"cluster_id": []byte(""),
363+
},
364+
},
365+
expectedClusterID: "",
366+
verbose: false,
367+
},
368+
}
369+
370+
for _, tc := range testCases {
371+
t.Run(tc.name, func(t *testing.T) {
372+
// Given
373+
var writer bytes.Buffer
374+
fakeClient := fake.NewSimpleClientset()
375+
kubeClient := &kube_fake.KubeClient{
376+
TestKubernetesInterface: fakeClient,
377+
}
378+
379+
if tc.secretExists {
380+
_, err := fakeClient.CoreV1().Secrets("kyma-system").Create(context.TODO(), tc.secret, metav1.CreateOptions{})
381+
assert.NoError(t, err)
382+
}
383+
384+
collector := diagnostics.NewMetadataCollector(kubeClient, &writer, tc.verbose)
385+
386+
// When
387+
metadata := collector.Run(context.TODO())
388+
389+
// Then
390+
assert.Equal(t, tc.expectedClusterID, metadata.ClusterID)
391+
392+
if tc.expectedErrorOutput != "" && tc.verbose {
393+
assert.Contains(t, writer.String(), tc.expectedErrorOutput)
394+
} else if !tc.verbose {
395+
assert.Empty(t, writer.String())
396+
}
397+
})
398+
}
399+
}

internal/diagnostics/noderesourceinfo.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,16 @@ type Usage struct {
6868
PodCount int `json:"podCount" yaml:"podCount"` // Number of running/pending pods on this node
6969
}
7070

71+
type Topology struct {
72+
Region string `json:"region" yaml:"region"`
73+
Zone string `json:"zone" yaml:"zone"`
74+
}
75+
7176
type NodeResourceInfo struct {
7277
MachineInfo MachineInfo `json:"machineInfo" yaml:"machineInfo"`
7378
Capacity Capacity `json:"capacity" yaml:"capacity"`
7479
Usage Usage `json:"usage" yaml:"usage"`
80+
Topology Topology `json:"topology" yaml:"topology"`
7581
}
7682

7783
type NodeResourceInfoCollector struct {
@@ -106,6 +112,7 @@ func (c *NodeResourceInfoCollector) Run(ctx context.Context) []NodeResourceInfo
106112
}
107113

108114
usage := c.calculateUsage(ctx, node, pods)
115+
topology := c.getTopologyData(node)
109116

110117
nodeInfo := NodeResourceInfo{
111118
MachineInfo: MachineInfo{
@@ -123,7 +130,8 @@ func (c *NodeResourceInfoCollector) Run(ctx context.Context) []NodeResourceInfo
123130
EphemeralStorage: node.Status.Capacity.StorageEphemeral().String(),
124131
Pods: node.Status.Capacity.Pods().String(),
125132
},
126-
Usage: usage,
133+
Usage: usage,
134+
Topology: topology,
127135
}
128136

129137
nodeResources = append(nodeResources, nodeInfo)
@@ -132,6 +140,13 @@ func (c *NodeResourceInfoCollector) Run(ctx context.Context) []NodeResourceInfo
132140
return nodeResources
133141
}
134142

143+
func (c *NodeResourceInfoCollector) getTopologyData(node corev1.Node) Topology {
144+
return Topology{
145+
Region: node.ObjectMeta.Labels["topology.kubernetes.io/region"],
146+
Zone: node.ObjectMeta.Labels["topology.kubernetes.io/zone"],
147+
}
148+
}
149+
135150
func (c *NodeResourceInfoCollector) getPodsOnNode(ctx context.Context, nodeName string) ([]corev1.Pod, error) {
136151
fieldSelector := fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName})
137152
pods, err := c.client.Static().CoreV1().

internal/diagnostics/noderesourceinfo_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ func TestRunWithMultipleNodesWithoutPods(t *testing.T) {
179179
assert.Equal(t, "1024.0Mi", result.Usage.MemoryUsage)
180180
assert.Equal(t, "5.5%", result.Usage.CPUUsagePercent)
181181
assert.Equal(t, "14.3%", result.Usage.MemoryUsagePercent)
182+
183+
// Should have topology labels
184+
assert.Equal(t, "region", result.Topology.Region)
185+
assert.Equal(t, "zone", result.Topology.Zone)
182186
}
183187

184188
assert.Empty(t, writer.String())
@@ -281,6 +285,10 @@ func createTestNode(name, arch, kernelVersion, osImage, containerRuntime, kubele
281285
return corev1.Node{
282286
ObjectMeta: metav1.ObjectMeta{
283287
Name: name,
288+
Labels: map[string]string{
289+
"topology.kubernetes.io/region": "region",
290+
"topology.kubernetes.io/zone": "zone",
291+
},
284292
},
285293
Status: corev1.NodeStatus{
286294
Capacity: corev1.ResourceList{

0 commit comments

Comments
 (0)