diff --git a/.chloggen/k8sattributes-container-attributes.yaml b/.chloggen/k8sattributes-container-attributes.yaml new file mode 100644 index 000000000000..446f7b8d024c --- /dev/null +++ b/.chloggen/k8sattributes-container-attributes.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: k8sattributesprocessor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: For pods with only one container, the `container.id` and `k8s.container.name` are not longer required in the resource attributes to add the container attributes + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [34189] + +# (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: [] diff --git a/processor/k8sattributesprocessor/README.md b/processor/k8sattributesprocessor/README.md index 425bef183f4a..7f0d9aef8475 100644 --- a/processor/k8sattributesprocessor/README.md +++ b/processor/k8sattributesprocessor/README.md @@ -98,7 +98,9 @@ are then also available for the use within association rules. Available attribut Not all the attributes are guaranteed to be added. Only attribute names from `metadata` should be used for pod_association's `resource_attribute`, because empty or non-existing values will be ignored. -Additional container level attributes can be extracted provided that certain resource attributes are provided: +Additional container level attributes can be extracted. If a pod contains more than one container, +either the `container.id`, or the `k8s.container.name` attribute must be provided in the incoming resource attributes to +correctly associate the matching container to the resource: 1. If the `container.id` resource attribute is provided, the following additional attributes will be available: - k8s.container.name diff --git a/processor/k8sattributesprocessor/processor.go b/processor/k8sattributesprocessor/processor.go index e656a41469bc..cfcc33c260f7 100644 --- a/processor/k8sattributesprocessor/processor.go +++ b/processor/k8sattributesprocessor/processor.go @@ -226,6 +226,15 @@ func (kp *kubernetesprocessor) addContainerAttributes(attrs pcommon.Map, pod *ku if !ok { return } + // if there is only one container in the pod, we can fall back to that container + case len(pod.Containers.ByID) == 1: + for _, c := range pod.Containers.ByID { + containerSpec = c + } + case len(pod.Containers.ByName) == 1: + for _, c := range pod.Containers.ByName { + containerSpec = c + } default: return } diff --git a/processor/k8sattributesprocessor/processor_test.go b/processor/k8sattributesprocessor/processor_test.go index 6e6458ea4c82..06b28caa7746 100644 --- a/processor/k8sattributesprocessor/processor_test.go +++ b/processor/k8sattributesprocessor/processor_test.go @@ -1264,6 +1264,80 @@ func TestProcessorAddContainerAttributes(t *testing.T) { conventions.AttributeContainerImageName: "test/app", }, }, + { + name: "fall back to only container", + op: func(kp *kubernetesprocessor) { + kp.podAssociations = []kube.Association{ + { + Name: "k8s.pod.uid", + Sources: []kube.AssociationSource{ + { + From: "resource_attribute", + Name: "k8s.pod.uid", + }, + }, + }, + } + kp.kc.(*fakeClient).Pods[newPodIdentifier("resource_attribute", "k8s.pod.uid", "19f651bc-73e4-410f-b3e9-f0241679d3b8")] = &kube.Pod{ + Containers: kube.PodContainers{ + ByName: map[string]*kube.Container{ + "app": { + Name: "app", + ImageName: "test/app", + ImageTag: "1.0.1", + }, + }, + }, + } + }, + resourceGens: []generateResourceFunc{ + withPodUID("19f651bc-73e4-410f-b3e9-f0241679d3b8"), + }, + wantAttrs: map[string]any{ + conventions.AttributeK8SPodUID: "19f651bc-73e4-410f-b3e9-f0241679d3b8", + conventions.AttributeK8SContainerName: "app", + conventions.AttributeContainerImageName: "test/app", + conventions.AttributeContainerImageTag: "1.0.1", + }, + }, + { + name: "multiple containers in the pod - do not fall back to any container", + op: func(kp *kubernetesprocessor) { + kp.podAssociations = []kube.Association{ + { + Name: "k8s.pod.uid", + Sources: []kube.AssociationSource{ + { + From: "resource_attribute", + Name: "k8s.pod.uid", + }, + }, + }, + } + kp.kc.(*fakeClient).Pods[newPodIdentifier("resource_attribute", "k8s.pod.uid", "19f651bc-73e4-410f-b3e9-f0241679d3b8")] = &kube.Pod{ + Containers: kube.PodContainers{ + ByName: map[string]*kube.Container{ + "app": { + Name: "app", + ImageName: "test/app", + ImageTag: "1.0.1", + }, + "app2": { + Name: "app2", + ImageName: "test/app", + ImageTag: "1.0.1", + }, + }, + }, + } + }, + resourceGens: []generateResourceFunc{ + withPodUID("19f651bc-73e4-410f-b3e9-f0241679d3b8"), + }, + wantAttrs: map[string]any{ + conventions.AttributeK8SPodUID: "19f651bc-73e4-410f-b3e9-f0241679d3b8", + }, + }, } for _, tt := range tests {