Skip to content

Commit 47ff222

Browse files
authored
[processor/resourcedetection] Add k8s.cluster.uid to kubeadm detector (#38216)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Add k8s.cluster.uid to kubeadm detector <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #38207 --------- Signed-off-by: odubajDT <[email protected]>
1 parent a778be9 commit 47ff222

File tree

13 files changed

+167
-19
lines changed

13 files changed

+167
-19
lines changed

.chloggen/kubeadm-uid.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: processor/resourcedetection
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "Add k8s.cluster.uid to kubeadm detector"
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [38207]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

internal/metadataproviders/kubeadm/metadata.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,58 @@ import (
1616
type Provider interface {
1717
// ClusterName returns the current K8S cluster name
1818
ClusterName(ctx context.Context) (string, error)
19+
// ClusterUID returns the current K8S cluster UID
20+
ClusterUID(ctx context.Context) (string, error)
1921
}
2022

2123
type LocalCache struct {
2224
ClusterName string
25+
ClusterUID string
2326
}
2427

2528
type kubeadmProvider struct {
26-
kubeadmClient kubernetes.Interface
27-
configMapName string
28-
configMapNamespace string
29-
cache LocalCache
29+
kubeadmClient kubernetes.Interface
30+
configMapName string
31+
kubeSystemNamespace string
32+
cache LocalCache
3033
}
3134

32-
func NewProvider(configMapName string, configMapNamespace string, apiConf k8sconfig.APIConfig) (Provider, error) {
35+
func NewProvider(configMapName string, kubeSystemNamespace string, apiConf k8sconfig.APIConfig) (Provider, error) {
3336
k8sAPIClient, err := k8sconfig.MakeClient(apiConf)
3437
if err != nil {
3538
return nil, fmt.Errorf("failed to create K8s API client: %w", err)
3639
}
3740
return &kubeadmProvider{
38-
kubeadmClient: k8sAPIClient,
39-
configMapName: configMapName,
40-
configMapNamespace: configMapNamespace,
41+
kubeadmClient: k8sAPIClient,
42+
configMapName: configMapName,
43+
kubeSystemNamespace: kubeSystemNamespace,
4144
}, nil
4245
}
4346

4447
func (k *kubeadmProvider) ClusterName(ctx context.Context) (string, error) {
4548
if k.cache.ClusterName != "" {
4649
return k.cache.ClusterName, nil
4750
}
48-
configmap, err := k.kubeadmClient.CoreV1().ConfigMaps(k.configMapNamespace).Get(ctx, k.configMapName, metav1.GetOptions{})
51+
configmap, err := k.kubeadmClient.CoreV1().ConfigMaps(k.kubeSystemNamespace).Get(ctx, k.configMapName, metav1.GetOptions{})
4952
if err != nil {
50-
return "", fmt.Errorf("failed to fetch ConfigMap with name %s and namespace %s from K8s API: %w", k.configMapName, k.configMapNamespace, err)
53+
return "", fmt.Errorf("failed to fetch ConfigMap with name %s and namespace %s from K8s API: %w", k.configMapName, k.kubeSystemNamespace, err)
5154
}
5255

5356
k.cache.ClusterName = configmap.Data["clusterName"]
5457

5558
return k.cache.ClusterName, nil
5659
}
60+
61+
func (k *kubeadmProvider) ClusterUID(ctx context.Context) (string, error) {
62+
if k.cache.ClusterUID != "" {
63+
return k.cache.ClusterUID, nil
64+
}
65+
ns, err := k.kubeadmClient.CoreV1().Namespaces().Get(ctx, k.kubeSystemNamespace, metav1.GetOptions{})
66+
if err != nil {
67+
return "", fmt.Errorf("failed to fetch Namespace %s from K8s API: %w", k.kubeSystemNamespace, err)
68+
}
69+
70+
k.cache.ClusterUID = string(ns.GetUID())
71+
72+
return k.cache.ClusterUID, nil
73+
}

internal/metadataproviders/kubeadm/metadata_test.go

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/stretchr/testify/assert"
1111
corev1 "k8s.io/api/core/v1"
1212
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
"k8s.io/apimachinery/pkg/types"
1314
"k8s.io/client-go/kubernetes/fake"
1415

1516
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig"
@@ -54,9 +55,9 @@ func TestClusterName(t *testing.T) {
5455
for _, tt := range tests {
5556
t.Run(tt.testName, func(t *testing.T) {
5657
kubeadmP := &kubeadmProvider{
57-
kubeadmClient: client,
58-
configMapName: tt.CMname,
59-
configMapNamespace: tt.CMnamespace,
58+
kubeadmClient: client,
59+
configMapName: tt.CMname,
60+
kubeSystemNamespace: tt.CMnamespace,
6061
}
6162
clusterName, err := kubeadmP.ClusterName(context.Background())
6263
if tt.errMsg != "" {
@@ -69,6 +70,47 @@ func TestClusterName(t *testing.T) {
6970
}
7071
}
7172

73+
func TestClusterUID(t *testing.T) {
74+
client := fake.NewSimpleClientset()
75+
err := setupNamespace(client)
76+
assert.NoError(t, err)
77+
78+
tests := []struct {
79+
testName string
80+
CMnamespace string
81+
clusterUID string
82+
errMsg string
83+
}{
84+
{
85+
testName: "valid",
86+
CMnamespace: "ns",
87+
clusterUID: "uid",
88+
errMsg: "",
89+
},
90+
{
91+
testName: "ns not found",
92+
CMnamespace: "ns2",
93+
errMsg: "failed to fetch Namespace ns2 from K8s API: namespaces \"ns2\" not found",
94+
},
95+
}
96+
97+
for _, tt := range tests {
98+
t.Run(tt.testName, func(t *testing.T) {
99+
kubeadmP := &kubeadmProvider{
100+
kubeadmClient: client,
101+
kubeSystemNamespace: tt.CMnamespace,
102+
}
103+
clusterName, err := kubeadmP.ClusterUID(context.Background())
104+
if tt.errMsg != "" {
105+
assert.EqualError(t, err, tt.errMsg)
106+
} else {
107+
assert.NoError(t, err)
108+
assert.Equal(t, tt.clusterUID, clusterName)
109+
}
110+
})
111+
}
112+
}
113+
72114
func setupConfigMap(client *fake.Clientset) error {
73115
cm := &corev1.ConfigMap{
74116
ObjectMeta: metav1.ObjectMeta{
@@ -85,3 +127,17 @@ func setupConfigMap(client *fake.Clientset) error {
85127
}
86128
return nil
87129
}
130+
131+
func setupNamespace(client *fake.Clientset) error {
132+
ns := &corev1.Namespace{
133+
ObjectMeta: metav1.ObjectMeta{
134+
UID: types.UID("uid"),
135+
Name: "ns",
136+
},
137+
}
138+
_, err := client.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{})
139+
if err != nil {
140+
return err
141+
}
142+
return nil
143+
}

processor/resourcedetectionprocessor/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,10 @@ rules:
479479
resources: ["configmaps"]
480480
resourceNames: ["kubeadm-config"]
481481
verbs: ["get"]
482+
- apiGroups: [""]
483+
resources: ["namespaces"]
484+
resourceNames: ["kube-system"]
485+
verbs: ["get"]
482486
---
483487
apiVersion: rbac.authorization.k8s.io/v1
484488
kind: RoleBinding

processor/resourcedetectionprocessor/internal/kubeadm/documentation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
| Name | Description | Values | Enabled |
1010
| ---- | ----------- | ------ | ------- |
1111
| k8s.cluster.name | The Kubernetes cluster name | Any Str | true |
12+
| k8s.cluster.uid | The Kubernetes cluster UID | Any Str | true |

processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config_test.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource_test.go

Lines changed: 8 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/testdata/config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ all_set:
33
resource_attributes:
44
k8s.cluster.name:
55
enabled: true
6+
k8s.cluster.uid:
7+
enabled: true
68
none_set:
79
resource_attributes:
810
k8s.cluster.name:
911
enabled: false
12+
k8s.cluster.uid:
13+
enabled: false

processor/resourcedetectionprocessor/internal/kubeadm/kubeadm.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import (
1818
)
1919

2020
const (
21-
TypeStr = "kubeadm"
22-
defaultConfigMapName = "kubeadm-config"
23-
defaultConfigMapNamespace = "kube-system"
21+
TypeStr = "kubeadm"
22+
defaultConfigMapName = "kubeadm-config"
23+
defaultKubeSystemNamespace = "kube-system"
2424
)
2525

2626
var _ internal.Detector = (*detector)(nil)
@@ -35,7 +35,7 @@ type detector struct {
3535
func NewDetector(set processor.Settings, dcfg internal.DetectorConfig) (internal.Detector, error) {
3636
cfg := dcfg.(Config)
3737

38-
kubeadmProvider, err := kubeadm.NewProvider(defaultConfigMapName, defaultConfigMapNamespace, cfg.APIConfig)
38+
kubeadmProvider, err := kubeadm.NewProvider(defaultConfigMapName, defaultKubeSystemNamespace, cfg.APIConfig)
3939
if err != nil {
4040
return nil, fmt.Errorf("failed creating kubeadm provider: %w", err)
4141
}
@@ -57,5 +57,13 @@ func (d *detector) Detect(ctx context.Context) (resource pcommon.Resource, schem
5757
d.rb.SetK8sClusterName(clusterName)
5858
}
5959

60+
if d.ra.K8sClusterUID.Enabled {
61+
clusterUID, err := d.provider.ClusterUID(ctx)
62+
if err != nil {
63+
return pcommon.NewResource(), "", fmt.Errorf("failed getting k8s cluster uid: %w", err)
64+
}
65+
d.rb.SetK8sClusterUID(clusterUID)
66+
}
67+
6068
return d.rb.Emit(), conventions.SchemaURL, nil
6169
}

processor/resourcedetectionprocessor/internal/kubeadm/kubeadm_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,15 @@ func (m *mockMetadata) ClusterName(_ context.Context) (string, error) {
2828
return args.String(0), args.Error(1)
2929
}
3030

31+
func (m *mockMetadata) ClusterUID(_ context.Context) (string, error) {
32+
args := m.MethodCalled("ClusterUID")
33+
return args.String(0), args.Error(1)
34+
}
35+
3136
func TestDetect(t *testing.T) {
3237
md := &mockMetadata{}
3338
md.On("ClusterName").Return("cluster-1", nil)
39+
md.On("ClusterUID").Return("uid-1", nil)
3440
cfg := CreateDefaultConfig()
3541
// set k8s cluster env variables and auth type to create a dummy API client
3642
cfg.APIConfig.AuthType = k8sconfig.AuthTypeNone
@@ -47,6 +53,7 @@ func TestDetect(t *testing.T) {
4753

4854
expected := map[string]any{
4955
conventions.AttributeK8SClusterName: "cluster-1",
56+
"k8s.cluster.uid": "uid-1",
5057
}
5158

5259
assert.Equal(t, expected, res.Attributes().AsRaw())
@@ -56,6 +63,7 @@ func TestDetectDisabledResourceAttributes(t *testing.T) {
5663
md := &mockMetadata{}
5764
cfg := CreateDefaultConfig()
5865
cfg.ResourceAttributes.K8sClusterName.Enabled = false
66+
cfg.ResourceAttributes.K8sClusterUID.Enabled = false
5967
// set k8s cluster env variables and auth type to create a dummy API client
6068
cfg.APIConfig.AuthType = k8sconfig.AuthTypeNone
6169
t.Setenv("KUBERNETES_SERVICE_HOST", "127.0.0.1")

processor/resourcedetectionprocessor/internal/kubeadm/metadata.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ resource_attributes:
77
description: The Kubernetes cluster name
88
type: string
99
enabled: true
10+
k8s.cluster.uid:
11+
description: The Kubernetes cluster UID
12+
type: string
13+
enabled: true

0 commit comments

Comments
 (0)