From a5a989a5a667cab3e072a93988931cf58e9861c6 Mon Sep 17 00:00:00 2001 From: ashiekh Date: Fri, 28 Mar 2025 15:23:41 -0700 Subject: [PATCH 1/7] Modified entity payloads to include additional attributes for the following entities Container - container.name - container.uid - k8s.pod.name - k8s.pod.uid Pod - k8s.namespace.name - k8s.pod.name Workload - k8s,namespace.name --- .../internal/constants/constants.go | 3 ++ .../internal/container/containers.go | 11 ++++- .../internal/container/containers_test.go | 41 ++++++++++++++++++- .../internal/cronjob/cronjobs_test.go | 1 + .../internal/metadata/metadata.go | 1 + .../internal/metadata/metadata_test.go | 2 + .../k8sclusterreceiver/internal/pod/pods.go | 5 ++- .../internal/pod/pods_test.go | 16 ++++++-- .../internal/statefulset/statefulsets_test.go | 1 + 9 files changed, 74 insertions(+), 7 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/constants/constants.go b/receiver/k8sclusterreceiver/internal/constants/constants.go index 191a51d365cad..e0f52404e712d 100644 --- a/receiver/k8sclusterreceiver/internal/constants/constants.go +++ b/receiver/k8sclusterreceiver/internal/constants/constants.go @@ -14,11 +14,14 @@ const ( K8sKeyReplicationControllerUID = "k8s.replicationcontroller.uid" K8sKeyResourceQuotaUID = "k8s.resourcequota.uid" K8sKeyClusterResourceQuotaUID = "openshift.clusterquota.uid" + K8sKeyPodUID = "k8s.pod.uid" // Resource labels keys for Name. K8sKeyReplicationControllerName = "k8s.replicationcontroller.name" K8sKeyResourceQuotaName = "k8s.resourcequota.name" K8sKeyClusterResourceQuotaName = "openshift.clusterquota.name" + K8sKeyNamespaceName = "k8s.namespace.name" + K8sKeyPodName = "k8s.pod.name" // Kubernetes resource kinds K8sKindCronJob = "CronJob" diff --git a/receiver/k8sclusterreceiver/internal/container/containers.go b/receiver/k8sclusterreceiver/internal/container/containers.go index 78253981a60bc..12d43b0e95d90 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers.go +++ b/receiver/k8sclusterreceiver/internal/container/containers.go @@ -13,6 +13,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/docker" metadataPkg "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/constants" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" imetadata "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/utils" @@ -23,6 +24,9 @@ const ( containerKeyStatus = "container.status" containerKeyStatusReason = "container.status.reason" containerCreationTimestamp = "container.creation_timestamp" + containerName = "container.name" + containerID = "container.uid" + containerImage = "container.image" // Values for container metadata containerStatusRunning = "running" @@ -96,9 +100,14 @@ func RecordSpecMetrics(logger *zap.Logger, mb *imetadata.MetricsBuilder, c corev mb.EmitForResource(imetadata.WithResource(rb.Emit())) } -func GetMetadata(cs corev1.ContainerStatus) *metadata.KubernetesMetadata { +func GetMetadata(pod *corev1.Pod, cs corev1.ContainerStatus) *metadata.KubernetesMetadata { mdata := map[string]string{} + mdata[containerName] = cs.Name + mdata[containerImage] = cs.Image + mdata[constants.K8sKeyPodName] = pod.Name + mdata[constants.K8sKeyPodUID] = string(pod.UID) + if cs.State.Running != nil { mdata[containerKeyStatus] = containerStatusRunning if !cs.State.Running.StartedAt.IsZero() { diff --git a/receiver/k8sclusterreceiver/internal/container/containers_test.go b/receiver/k8sclusterreceiver/internal/container/containers_test.go index 030aca08b2fd1..36e85d0a6178f 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers_test.go +++ b/receiver/k8sclusterreceiver/internal/container/containers_test.go @@ -7,20 +7,35 @@ import ( "testing" "time" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/constants" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" ) func TestGetMetadata(t *testing.T) { refTime := v1.Now() + pod := &corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "test-pod", + Namespace: "test-namespace", + UID: types.UID("test-pod-uid"), + }, + } + tests := []struct { name string containerState corev1.ContainerState expectedStatus string expectedReason string expectedStartedAt string + containerName string + containerID string + containerImage string + podName string + podUID string }{ { name: "Running container", @@ -31,6 +46,11 @@ func TestGetMetadata(t *testing.T) { }, expectedStatus: containerStatusRunning, expectedStartedAt: refTime.Format(time.RFC3339), + containerName: "my-test-container1", + containerID: "f37ee861-f093-4cea-aa26-f39fff8b0998", + containerImage: "docker/someimage1", + podName: pod.Name, + podUID: string(pod.UID), }, { name: "Terminated container", @@ -46,6 +66,11 @@ func TestGetMetadata(t *testing.T) { expectedStatus: containerStatusTerminated, expectedReason: "Completed", expectedStartedAt: refTime.Format(time.RFC3339), + containerName: "my-test-container2", + containerID: "f37ee861-f093-4cea-aa26-f39fff8b0997", + containerImage: "docker/someimage2", + podName: pod.Name, + podUID: string(pod.UID), }, { name: "Waiting container", @@ -56,15 +81,23 @@ func TestGetMetadata(t *testing.T) { }, expectedStatus: containerStatusWaiting, expectedReason: "CrashLoopBackOff", + containerName: "my-test-container3", + containerID: "f37ee861-f093-4cea-aa26-f39fff8b0996", + containerImage: "docker/someimage3", + podName: pod.Name, + podUID: string(pod.UID), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cs := corev1.ContainerStatus{ - State: tt.containerState, + State: tt.containerState, + Name: tt.containerName, + ContainerID: tt.containerID, + Image: tt.containerImage, } - md := GetMetadata(cs) + md := GetMetadata(pod, cs) require.NotNil(t, md) assert.Equal(t, tt.expectedStatus, md.Metadata[containerKeyStatus]) @@ -75,6 +108,10 @@ func TestGetMetadata(t *testing.T) { assert.Contains(t, md.Metadata, containerCreationTimestamp) assert.Equal(t, tt.expectedStartedAt, md.Metadata[containerCreationTimestamp]) } + assert.Equal(t, tt.containerName, md.Metadata[containerName]) + assert.Equal(t, tt.containerImage, md.Metadata[containerImage]) + assert.Equal(t, tt.podName, md.Metadata[constants.K8sKeyPodName]) + assert.Equal(t, tt.podUID, md.Metadata[constants.K8sKeyPodUID]) }) } } diff --git a/receiver/k8sclusterreceiver/internal/cronjob/cronjobs_test.go b/receiver/k8sclusterreceiver/internal/cronjob/cronjobs_test.go index d89cfa5843149..1e79c32fb1223 100644 --- a/receiver/k8sclusterreceiver/internal/cronjob/cronjobs_test.go +++ b/receiver/k8sclusterreceiver/internal/cronjob/cronjobs_test.go @@ -59,6 +59,7 @@ func TestCronJobMetadata(t *testing.T) { "concurrency_policy": "concurrency_policy", "k8s.workload.kind": "CronJob", "k8s.workload.name": "test-cronjob-1", + "k8s.namespace.name": "test-namespace", }, }, *actualMetadata["test-cronjob-1-uid"], diff --git a/receiver/k8sclusterreceiver/internal/metadata/metadata.go b/receiver/k8sclusterreceiver/internal/metadata/metadata.go index 5ea50a953f7b5..8f5442b7bd6c8 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/metadata.go +++ b/receiver/k8sclusterreceiver/internal/metadata/metadata.go @@ -55,6 +55,7 @@ func GetGenericMetadata(om *v1.ObjectMeta, resourceType string) *KubernetesMetad metadata[constants.K8sKeyWorkLoadKind] = resourceType metadata[constants.K8sKeyWorkLoadName] = om.Name metadata[rType+".creation_timestamp"] = om.GetCreationTimestamp().Format(time.RFC3339) + metadata[constants.K8sKeyNamespaceName] = om.Namespace for _, or := range om.OwnerReferences { kind := strings.ToLower(or.Kind) diff --git a/receiver/k8sclusterreceiver/internal/metadata/metadata_test.go b/receiver/k8sclusterreceiver/internal/metadata/metadata_test.go index 8f7015a12ac07..26a49ebc0645e 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/metadata_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/metadata_test.go @@ -19,6 +19,7 @@ func Test_getGenericMetadata(t *testing.T) { om := &v1.ObjectMeta{ Name: "test-name", UID: "test-uid", + Namespace: "test-namespace", Generation: 0, CreationTimestamp: v1.NewTime(now), Labels: map[string]string{ @@ -46,6 +47,7 @@ func Test_getGenericMetadata(t *testing.T) { assert.Equal(t, map[string]string{ "k8s.workload.name": "test-name", "k8s.workload.kind": "ResourceType", + "k8s.namespace.name": "test-namespace", "resourcetype.creation_timestamp": now.Format(time.RFC3339), "k8s.owner-kind-1.name": "owner1", "k8s.owner-kind-1.uid": "owner1", diff --git a/receiver/k8sclusterreceiver/internal/pod/pods.go b/receiver/k8sclusterreceiver/internal/pod/pods.go index 263e4c3ac5f0c..055942b3ab49b 100644 --- a/receiver/k8sclusterreceiver/internal/pod/pods.go +++ b/receiver/k8sclusterreceiver/internal/pod/pods.go @@ -164,6 +164,9 @@ func GetMetadata(pod *corev1.Pod, mc *metadata.Store, logger *zap.Logger) map[ex meta = maps.MergeStringMaps(meta, collectPodReplicaSetProperties(pod, store, logger)) } + meta[constants.K8sKeyNamespaceName] = pod.Namespace + meta[constants.K8sKeyPodName] = pod.Name + podID := experimentalmetricmetadata.ResourceID(pod.UID) return metadata.MergeKubernetesMetadataMaps(map[experimentalmetricmetadata.ResourceID]*metadata.KubernetesMetadata{ podID: { @@ -252,7 +255,7 @@ func getWorkloadProperties(ref *v1.OwnerReference, labelKey string) map[string]s func getPodContainerProperties(pod *corev1.Pod) map[experimentalmetricmetadata.ResourceID]*metadata.KubernetesMetadata { km := map[experimentalmetricmetadata.ResourceID]*metadata.KubernetesMetadata{} for _, cs := range pod.Status.ContainerStatuses { - md := container.GetMetadata(cs) + md := container.GetMetadata(pod, cs) km[md.ResourceID] = md } return km diff --git a/receiver/k8sclusterreceiver/internal/pod/pods_test.go b/receiver/k8sclusterreceiver/internal/pod/pods_test.go index 4013207fdf8af..2e1f456733735 100644 --- a/receiver/k8sclusterreceiver/internal/pod/pods_test.go +++ b/receiver/k8sclusterreceiver/internal/pod/pods_test.go @@ -235,8 +235,10 @@ func testCaseForPodWorkload(to testCaseOptions) testCase { func expectedKubernetesMetadata(to testCaseOptions) map[experimentalmetricmetadata.ResourceID]*metadata.KubernetesMetadata { podUIDLabel := "test-pod-0-uid" + podNameLabel := "test-pod-0" kindLower := strings.ToLower(to.kind) kindObjName := fmt.Sprintf("test-%s-0", kindLower) + namespaceLabel := "test-namespace" kindObjUID := fmt.Sprintf("test-%s-0-uid", kindLower) kindNameLabel := fmt.Sprintf("k8s.%s.name", kindLower) kindUIDLabel := fmt.Sprintf("k8s.%s.uid", kindLower) @@ -247,9 +249,11 @@ func expectedKubernetesMetadata(to testCaseOptions) map[experimentalmetricmetada ResourceIDKey: "k8s.pod.uid", ResourceID: experimentalmetricmetadata.ResourceID(podUIDLabel), Metadata: map[string]string{ - kindNameLabel: kindObjName, - kindUIDLabel: kindObjUID, - "k8s.pod.phase": "Unknown", // Default value when phase is not set. + kindNameLabel: kindObjName, + kindUIDLabel: kindObjUID, + "k8s.pod.phase": "Unknown", // Default value when phase is not set. + "k8s.namespace.name": namespaceLabel, + "k8s.pod.name": podNameLabel, }, }, } @@ -485,6 +489,8 @@ func TestTransform(t *testing.T) { func TestPodMetadata(t *testing.T) { tests := []struct { name string + podName string + namespace string statusPhase corev1.PodPhase statusReason string expectedMetadata map[string]string @@ -494,6 +500,8 @@ func TestPodMetadata(t *testing.T) { statusPhase: corev1.PodFailed, statusReason: "Evicted", expectedMetadata: map[string]string{ + "k8s.pod.name": "test-pod-0", + "k8s.namespace.name": "test-namespace", "k8s.pod.phase": "Failed", "k8s.pod.status_reason": "Evicted", "k8s.workload.kind": "Deployment", @@ -509,6 +517,8 @@ func TestPodMetadata(t *testing.T) { statusPhase: corev1.PodRunning, statusReason: "", expectedMetadata: map[string]string{ + "k8s.pod.name": "test-pod-0", + "k8s.namespace.name": "test-namespace", "k8s.pod.phase": "Running", "k8s.workload.kind": "Deployment", "k8s.workload.name": "test-deployment-0", diff --git a/receiver/k8sclusterreceiver/internal/statefulset/statefulsets_test.go b/receiver/k8sclusterreceiver/internal/statefulset/statefulsets_test.go index 5cdc19fc47768..4f2aaaaecbfbe 100644 --- a/receiver/k8sclusterreceiver/internal/statefulset/statefulsets_test.go +++ b/receiver/k8sclusterreceiver/internal/statefulset/statefulsets_test.go @@ -66,6 +66,7 @@ func TestStatefulsetMetadata(t *testing.T) { Metadata: map[string]string{ "k8s.workload.name": "test-statefulset-1", "k8s.workload.kind": "StatefulSet", + "k8s.namespace.name": "test-namespace", "statefulset.creation_timestamp": "0001-01-01T00:00:00Z", "foo": "bar", "foo1": "", From 6ebe7d853a0b158505faeddb302424a70fdf69e6 Mon Sep 17 00:00:00 2001 From: ashiekh Date: Fri, 28 Mar 2025 15:56:44 -0700 Subject: [PATCH 2/7] Added changelog file --- .chloggen/partial-fix-issue-39038.yaml | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .chloggen/partial-fix-issue-39038.yaml diff --git a/.chloggen/partial-fix-issue-39038.yaml b/.chloggen/partial-fix-issue-39038.yaml new file mode 100644 index 0000000000000..9acbd1867b5fc --- /dev/null +++ b/.chloggen/partial-fix-issue-39038.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: k8sclusterreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add missing attributes to entities in experimental entity feature + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [39038] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] From fb6c99d1e8579b3b69c8beef52bbdff92d35a57a Mon Sep 17 00:00:00 2001 From: ashiekh Date: Fri, 28 Mar 2025 17:20:26 -0700 Subject: [PATCH 3/7] fixed tests in watcher_test.go --- .../internal/container/containers_test.go | 3 ++- receiver/k8sclusterreceiver/watcher_test.go | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/container/containers_test.go b/receiver/k8sclusterreceiver/internal/container/containers_test.go index 36e85d0a6178f..d58614b2e76c0 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers_test.go +++ b/receiver/k8sclusterreceiver/internal/container/containers_test.go @@ -7,12 +7,13 @@ import ( "testing" "time" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/constants" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/constants" ) func TestGetMetadata(t *testing.T) { diff --git a/receiver/k8sclusterreceiver/watcher_test.go b/receiver/k8sclusterreceiver/watcher_test.go index 308c00158a12a..b00b0bf85d91b 100644 --- a/receiver/k8sclusterreceiver/watcher_test.go +++ b/receiver/k8sclusterreceiver/watcher_test.go @@ -270,7 +270,7 @@ func TestSyncMetadataAndEmitEntityEvents(t *testing.T) { "otel.entity.interval": int64(7200000), // 2h in milliseconds "otel.entity.type": "k8s.pod", "otel.entity.id": map[string]any{"k8s.pod.uid": "pod0"}, - "otel.entity.attributes": map[string]any{"pod.creation_timestamp": "0001-01-01T00:00:00Z", "k8s.pod.phase": "Unknown"}, + "otel.entity.attributes": map[string]any{"pod.creation_timestamp": "0001-01-01T00:00:00Z", "k8s.pod.phase": "Unknown", "k8s.namespace.name": "test", "k8s.pod.name": "0"}, } assert.EqualValues(t, expected, lr.Attributes().AsRaw()) assert.WithinRange(t, lr.Timestamp().AsTime(), step1, step2) @@ -324,7 +324,7 @@ func TestObjMetadata(t *testing.T) { EntityType: "k8s.pod", ResourceIDKey: "k8s.pod.uid", ResourceID: "test-pod-0-uid", - Metadata: allPodMetadata(map[string]string{"k8s.pod.phase": "Succeeded"}), + Metadata: allPodMetadata(map[string]string{"k8s.pod.phase": "Succeeded", "k8s.pod.name": "test-pod-0", "k8s.namespace.name": "test-namespace"}), }, experimentalmetricmetadata.ResourceID("container-id"): { EntityType: "container", @@ -333,6 +333,10 @@ func TestObjMetadata(t *testing.T) { Metadata: map[string]string{ "container.status": "running", "container.creation_timestamp": "0001-01-01T01:01:01Z", + "container.image": "container-image-name", + "container.name": "container-name", + "k8s.pod.name": "test-pod-0", + "k8s.pod.uid": "test-pod-0-uid", }, }, }, @@ -359,6 +363,8 @@ func TestObjMetadata(t *testing.T) { "k8s.statefulset.uid": "test-statefulset-0-uid", "k8s.pod.phase": "Failed", "k8s.pod.status_reason": "Evicted", + "k8s.pod.name": "test-pod-0", + "k8s.namespace.name": "test-namespace", }), }, }, @@ -398,6 +404,8 @@ func TestObjMetadata(t *testing.T) { "k8s.service.test-service": "", "k8s-app": "my-app", "k8s.pod.phase": "Running", + "k8s.namespace.name": "test-namespace", + "k8s.pod.name": "test-pod-0", }), }, }, @@ -415,6 +423,7 @@ func TestObjMetadata(t *testing.T) { "k8s.workload.kind": "DaemonSet", "k8s.workload.name": "test-daemonset-1", "daemonset.creation_timestamp": "0001-01-01T00:00:00Z", + "k8s.namespace.name": "test-namespace", }, }, }, @@ -433,6 +442,7 @@ func TestObjMetadata(t *testing.T) { "k8s.workload.name": "test-deployment-1", "k8s.deployment.name": "test-deployment-1", "deployment.creation_timestamp": "0001-01-01T00:00:00Z", + "k8s.namespace.name": "test-namespace", }, }, }, @@ -450,6 +460,7 @@ func TestObjMetadata(t *testing.T) { "k8s.workload.kind": "HPA", "k8s.workload.name": "test-hpa-1", "hpa.creation_timestamp": "0001-01-01T00:00:00Z", + "k8s.namespace.name": "test-namespace", }, }, }, @@ -469,6 +480,7 @@ func TestObjMetadata(t *testing.T) { "k8s.workload.kind": "Job", "k8s.workload.name": "test-job-1", "job.creation_timestamp": "0001-01-01T00:00:00Z", + "k8s.namespace.name": "test-namespace", }, }, }, @@ -511,6 +523,7 @@ func TestObjMetadata(t *testing.T) { "k8s.workload.kind": "ReplicaSet", "k8s.workload.name": "test-replicaset-1", "replicaset.creation_timestamp": "0001-01-01T00:00:00Z", + "k8s.namespace.name": "test-namespace", }, }, }, @@ -534,6 +547,7 @@ func TestObjMetadata(t *testing.T) { "k8s.workload.kind": "ReplicationController", "k8s.workload.name": "test-replicationcontroller-1", "replicationcontroller.creation_timestamp": "0001-01-01T00:00:00Z", + "k8s.namespace.name": "test-namespace", }, }, }, From 25d37578575857323588c423842adf303bd41e70 Mon Sep 17 00:00:00 2001 From: ashiekh Date: Wed, 2 Apr 2025 10:20:05 -0700 Subject: [PATCH 4/7] Removed unused constant containerID. Renamed containerImage value to match semantic conventions --- receiver/k8sclusterreceiver/internal/container/containers.go | 3 +-- receiver/k8sclusterreceiver/watcher_test.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/container/containers.go b/receiver/k8sclusterreceiver/internal/container/containers.go index 12d43b0e95d90..b7047b2857b10 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers.go +++ b/receiver/k8sclusterreceiver/internal/container/containers.go @@ -25,8 +25,7 @@ const ( containerKeyStatusReason = "container.status.reason" containerCreationTimestamp = "container.creation_timestamp" containerName = "container.name" - containerID = "container.uid" - containerImage = "container.image" + containerImage = "container.image.name" // Values for container metadata containerStatusRunning = "running" diff --git a/receiver/k8sclusterreceiver/watcher_test.go b/receiver/k8sclusterreceiver/watcher_test.go index b00b0bf85d91b..66b9b176821d0 100644 --- a/receiver/k8sclusterreceiver/watcher_test.go +++ b/receiver/k8sclusterreceiver/watcher_test.go @@ -333,7 +333,7 @@ func TestObjMetadata(t *testing.T) { Metadata: map[string]string{ "container.status": "running", "container.creation_timestamp": "0001-01-01T01:01:01Z", - "container.image": "container-image-name", + "container.image.name": "container-image-name", "container.name": "container-name", "k8s.pod.name": "test-pod-0", "k8s.pod.uid": "test-pod-0-uid", From 3d878391b3e9461cf8810c78595e51acc3871100 Mon Sep 17 00:00:00 2001 From: ashiekh Date: Wed, 2 Apr 2025 19:27:18 -0700 Subject: [PATCH 5/7] Changed attribute label from 'container.name' to 'k8s.container.name' --- receiver/k8sclusterreceiver/internal/container/containers.go | 2 +- receiver/k8sclusterreceiver/watcher_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/container/containers.go b/receiver/k8sclusterreceiver/internal/container/containers.go index b7047b2857b10..6b050d0c30012 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers.go +++ b/receiver/k8sclusterreceiver/internal/container/containers.go @@ -24,7 +24,7 @@ const ( containerKeyStatus = "container.status" containerKeyStatusReason = "container.status.reason" containerCreationTimestamp = "container.creation_timestamp" - containerName = "container.name" + containerName = "k8s.container.name" containerImage = "container.image.name" // Values for container metadata diff --git a/receiver/k8sclusterreceiver/watcher_test.go b/receiver/k8sclusterreceiver/watcher_test.go index 66b9b176821d0..34494510bc106 100644 --- a/receiver/k8sclusterreceiver/watcher_test.go +++ b/receiver/k8sclusterreceiver/watcher_test.go @@ -334,7 +334,7 @@ func TestObjMetadata(t *testing.T) { "container.status": "running", "container.creation_timestamp": "0001-01-01T01:01:01Z", "container.image.name": "container-image-name", - "container.name": "container-name", + "k8s.container.name": "container-name", "k8s.pod.name": "test-pod-0", "k8s.pod.uid": "test-pod-0-uid", }, From c21b10a0b5a192d016ea9f3481f6def709cbc5bd Mon Sep 17 00:00:00 2001 From: ashiekh Date: Wed, 2 Apr 2025 20:10:58 -0700 Subject: [PATCH 6/7] Split container image into separate attributes viz container.image.name and container.image.tag. --- .../internal/container/containers.go | 14 +++- .../internal/container/containers_test.go | 80 +++++++++++-------- .../k8sclusterreceiver/internal/pod/pods.go | 6 +- receiver/k8sclusterreceiver/watcher_test.go | 1 + 4 files changed, 60 insertions(+), 41 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/container/containers.go b/receiver/k8sclusterreceiver/internal/container/containers.go index 6b050d0c30012..59c0143b1133a 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers.go +++ b/receiver/k8sclusterreceiver/internal/container/containers.go @@ -25,7 +25,8 @@ const ( containerKeyStatusReason = "container.status.reason" containerCreationTimestamp = "container.creation_timestamp" containerName = "k8s.container.name" - containerImage = "container.image.name" + containerImageName = "container.image.name" + containerImageTag = "container.image.tag" // Values for container metadata containerStatusRunning = "running" @@ -99,11 +100,18 @@ func RecordSpecMetrics(logger *zap.Logger, mb *imetadata.MetricsBuilder, c corev mb.EmitForResource(imetadata.WithResource(rb.Emit())) } -func GetMetadata(pod *corev1.Pod, cs corev1.ContainerStatus) *metadata.KubernetesMetadata { +func GetMetadata(pod *corev1.Pod, cs corev1.ContainerStatus, logger *zap.Logger) *metadata.KubernetesMetadata { mdata := map[string]string{} + imageStr := cs.Image + image, err := docker.ParseImageName(cs.Image) + if err != nil { + docker.LogParseError(err, imageStr, logger) + } else { + mdata[containerImageName] = image.Repository + mdata[containerImageTag] = image.Tag + } mdata[containerName] = cs.Name - mdata[containerImage] = cs.Image mdata[constants.K8sKeyPodName] = pod.Name mdata[constants.K8sKeyPodUID] = string(pod.UID) diff --git a/receiver/k8sclusterreceiver/internal/container/containers_test.go b/receiver/k8sclusterreceiver/internal/container/containers_test.go index d58614b2e76c0..b2178bb5f53e1 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers_test.go +++ b/receiver/k8sclusterreceiver/internal/container/containers_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/zap" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -27,16 +28,18 @@ func TestGetMetadata(t *testing.T) { } tests := []struct { - name string - containerState corev1.ContainerState - expectedStatus string - expectedReason string - expectedStartedAt string - containerName string - containerID string - containerImage string - podName string - podUID string + name string + containerState corev1.ContainerState + expectedStatus string + expectedReason string + expectedStartedAt string + containerName string + containerID string + containerImage string + containerImageName string + containerImageTag string + podName string + podUID string }{ { name: "Running container", @@ -45,13 +48,15 @@ func TestGetMetadata(t *testing.T) { StartedAt: refTime, }, }, - expectedStatus: containerStatusRunning, - expectedStartedAt: refTime.Format(time.RFC3339), - containerName: "my-test-container1", - containerID: "f37ee861-f093-4cea-aa26-f39fff8b0998", - containerImage: "docker/someimage1", - podName: pod.Name, - podUID: string(pod.UID), + expectedStatus: containerStatusRunning, + expectedStartedAt: refTime.Format(time.RFC3339), + containerName: "my-test-container1", + containerID: "f37ee861-f093-4cea-aa26-f39fff8b0998", + containerImage: "docker/someimage1:v1.0", + containerImageName: "docker/someimage1", + containerImageTag: "v1.0", + podName: pod.Name, + podUID: string(pod.UID), }, { name: "Terminated container", @@ -64,14 +69,16 @@ func TestGetMetadata(t *testing.T) { ExitCode: 0, }, }, - expectedStatus: containerStatusTerminated, - expectedReason: "Completed", - expectedStartedAt: refTime.Format(time.RFC3339), - containerName: "my-test-container2", - containerID: "f37ee861-f093-4cea-aa26-f39fff8b0997", - containerImage: "docker/someimage2", - podName: pod.Name, - podUID: string(pod.UID), + expectedStatus: containerStatusTerminated, + expectedReason: "Completed", + expectedStartedAt: refTime.Format(time.RFC3339), + containerName: "my-test-container2", + containerID: "f37ee861-f093-4cea-aa26-f39fff8b0997", + containerImage: "docker/someimage2:v1.1", + containerImageName: "docker/someimage2", + containerImageTag: "v1.1", + podName: pod.Name, + podUID: string(pod.UID), }, { name: "Waiting container", @@ -80,16 +87,18 @@ func TestGetMetadata(t *testing.T) { Reason: "CrashLoopBackOff", }, }, - expectedStatus: containerStatusWaiting, - expectedReason: "CrashLoopBackOff", - containerName: "my-test-container3", - containerID: "f37ee861-f093-4cea-aa26-f39fff8b0996", - containerImage: "docker/someimage3", - podName: pod.Name, - podUID: string(pod.UID), + expectedStatus: containerStatusWaiting, + expectedReason: "CrashLoopBackOff", + containerName: "my-test-container3", + containerID: "f37ee861-f093-4cea-aa26-f39fff8b0996", + containerImage: "docker/someimage3:latest", + containerImageName: "docker/someimage3", + containerImageTag: "latest", + podName: pod.Name, + podUID: string(pod.UID), }, } - + logger := zap.NewNop() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cs := corev1.ContainerStatus{ @@ -98,7 +107,7 @@ func TestGetMetadata(t *testing.T) { ContainerID: tt.containerID, Image: tt.containerImage, } - md := GetMetadata(pod, cs) + md := GetMetadata(pod, cs, logger) require.NotNil(t, md) assert.Equal(t, tt.expectedStatus, md.Metadata[containerKeyStatus]) @@ -110,7 +119,8 @@ func TestGetMetadata(t *testing.T) { assert.Equal(t, tt.expectedStartedAt, md.Metadata[containerCreationTimestamp]) } assert.Equal(t, tt.containerName, md.Metadata[containerName]) - assert.Equal(t, tt.containerImage, md.Metadata[containerImage]) + assert.Equal(t, tt.containerImageName, md.Metadata[containerImageName]) + assert.Equal(t, tt.containerImageTag, md.Metadata[containerImageTag]) assert.Equal(t, tt.podName, md.Metadata[constants.K8sKeyPodName]) assert.Equal(t, tt.podUID, md.Metadata[constants.K8sKeyPodUID]) }) diff --git a/receiver/k8sclusterreceiver/internal/pod/pods.go b/receiver/k8sclusterreceiver/internal/pod/pods.go index 055942b3ab49b..af38f5f26a60d 100644 --- a/receiver/k8sclusterreceiver/internal/pod/pods.go +++ b/receiver/k8sclusterreceiver/internal/pod/pods.go @@ -175,7 +175,7 @@ func GetMetadata(pod *corev1.Pod, mc *metadata.Store, logger *zap.Logger) map[ex ResourceID: podID, Metadata: meta, }, - }, getPodContainerProperties(pod)) + }, getPodContainerProperties(pod, logger)) } // collectPodJobProperties checks if pod owner of type Job is cached. Check owners reference @@ -252,10 +252,10 @@ func getWorkloadProperties(ref *v1.OwnerReference, labelKey string) map[string]s } } -func getPodContainerProperties(pod *corev1.Pod) map[experimentalmetricmetadata.ResourceID]*metadata.KubernetesMetadata { +func getPodContainerProperties(pod *corev1.Pod, logger *zap.Logger) map[experimentalmetricmetadata.ResourceID]*metadata.KubernetesMetadata { km := map[experimentalmetricmetadata.ResourceID]*metadata.KubernetesMetadata{} for _, cs := range pod.Status.ContainerStatuses { - md := container.GetMetadata(pod, cs) + md := container.GetMetadata(pod, cs, logger) km[md.ResourceID] = md } return km diff --git a/receiver/k8sclusterreceiver/watcher_test.go b/receiver/k8sclusterreceiver/watcher_test.go index 34494510bc106..72bfeb9b2479b 100644 --- a/receiver/k8sclusterreceiver/watcher_test.go +++ b/receiver/k8sclusterreceiver/watcher_test.go @@ -334,6 +334,7 @@ func TestObjMetadata(t *testing.T) { "container.status": "running", "container.creation_timestamp": "0001-01-01T01:01:01Z", "container.image.name": "container-image-name", + "container.image.tag": "latest", "k8s.container.name": "container-name", "k8s.pod.name": "test-pod-0", "k8s.pod.uid": "test-pod-0-uid", From be1eec78d28a579c4e5eb974d5c5da01d1941d46 Mon Sep 17 00:00:00 2001 From: ashiekh Date: Wed, 2 Apr 2025 23:15:28 -0700 Subject: [PATCH 7/7] Include k8s.namespace.name and k8s.node.name into the metadata --- .../internal/constants/constants.go | 1 + .../internal/container/containers.go | 2 ++ .../internal/container/containers_test.go | 13 +++++++++++++ receiver/k8sclusterreceiver/watcher_test.go | 2 ++ 4 files changed, 18 insertions(+) diff --git a/receiver/k8sclusterreceiver/internal/constants/constants.go b/receiver/k8sclusterreceiver/internal/constants/constants.go index e0f52404e712d..0ed4c3838df03 100644 --- a/receiver/k8sclusterreceiver/internal/constants/constants.go +++ b/receiver/k8sclusterreceiver/internal/constants/constants.go @@ -22,6 +22,7 @@ const ( K8sKeyClusterResourceQuotaName = "openshift.clusterquota.name" K8sKeyNamespaceName = "k8s.namespace.name" K8sKeyPodName = "k8s.pod.name" + K8sKeyNodeName = "k8s.node.name" // Kubernetes resource kinds K8sKindCronJob = "CronJob" diff --git a/receiver/k8sclusterreceiver/internal/container/containers.go b/receiver/k8sclusterreceiver/internal/container/containers.go index 59c0143b1133a..e7668b37c9e2b 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers.go +++ b/receiver/k8sclusterreceiver/internal/container/containers.go @@ -114,6 +114,8 @@ func GetMetadata(pod *corev1.Pod, cs corev1.ContainerStatus, logger *zap.Logger) mdata[containerName] = cs.Name mdata[constants.K8sKeyPodName] = pod.Name mdata[constants.K8sKeyPodUID] = string(pod.UID) + mdata[constants.K8sKeyNamespaceName] = pod.Namespace + mdata[constants.K8sKeyNodeName] = pod.Spec.NodeName if cs.State.Running != nil { mdata[containerKeyStatus] = containerStatusRunning diff --git a/receiver/k8sclusterreceiver/internal/container/containers_test.go b/receiver/k8sclusterreceiver/internal/container/containers_test.go index b2178bb5f53e1..d2f6ccd2f3c23 100644 --- a/receiver/k8sclusterreceiver/internal/container/containers_test.go +++ b/receiver/k8sclusterreceiver/internal/container/containers_test.go @@ -25,6 +25,9 @@ func TestGetMetadata(t *testing.T) { Namespace: "test-namespace", UID: types.UID("test-pod-uid"), }, + Spec: corev1.PodSpec{ + NodeName: "test-node", + }, } tests := []struct { @@ -40,6 +43,8 @@ func TestGetMetadata(t *testing.T) { containerImageTag string podName string podUID string + nodeName string + namespaceName string }{ { name: "Running container", @@ -57,6 +62,8 @@ func TestGetMetadata(t *testing.T) { containerImageTag: "v1.0", podName: pod.Name, podUID: string(pod.UID), + namespaceName: "test-namespace", + nodeName: "test-node", }, { name: "Terminated container", @@ -79,6 +86,8 @@ func TestGetMetadata(t *testing.T) { containerImageTag: "v1.1", podName: pod.Name, podUID: string(pod.UID), + namespaceName: "test-namespace", + nodeName: "test-node", }, { name: "Waiting container", @@ -96,6 +105,8 @@ func TestGetMetadata(t *testing.T) { containerImageTag: "latest", podName: pod.Name, podUID: string(pod.UID), + namespaceName: "test-namespace", + nodeName: "test-node", }, } logger := zap.NewNop() @@ -123,6 +134,8 @@ func TestGetMetadata(t *testing.T) { assert.Equal(t, tt.containerImageTag, md.Metadata[containerImageTag]) assert.Equal(t, tt.podName, md.Metadata[constants.K8sKeyPodName]) assert.Equal(t, tt.podUID, md.Metadata[constants.K8sKeyPodUID]) + assert.Equal(t, tt.namespaceName, md.Metadata[constants.K8sKeyNamespaceName]) + assert.Equal(t, tt.nodeName, md.Metadata[constants.K8sKeyNodeName]) }) } } diff --git a/receiver/k8sclusterreceiver/watcher_test.go b/receiver/k8sclusterreceiver/watcher_test.go index 72bfeb9b2479b..104f0176298a6 100644 --- a/receiver/k8sclusterreceiver/watcher_test.go +++ b/receiver/k8sclusterreceiver/watcher_test.go @@ -338,6 +338,8 @@ func TestObjMetadata(t *testing.T) { "k8s.container.name": "container-name", "k8s.pod.name": "test-pod-0", "k8s.pod.uid": "test-pod-0-uid", + "k8s.namespace.name": "test-namespace", + "k8s.node.name": "test-node", }, }, },