From 528678e607b60b00cbb5432b7ba8d2fcdc8cb810 Mon Sep 17 00:00:00 2001 From: ET <evantorrie@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:12:13 -0700 Subject: [PATCH] [processor/k8sattributes] Add `container.image.repo_digests` (#34031) **Description:** Adds support for extracting canonical repo digest image references for a container from k8s attributes **Link to tracking Issue:** #34029 **Testing:** 1. Adds unit tests for extracting the k8s `.imageID` field from container status API if it is of the `reference.Canonical` form, and storing it in a `ImageRepoDigest` string field. (Note: k8s will only ever return one value here, although the semantic conventions for `container.image.repo_digests` define it to be a `Slice`. We convert into a Slice lazily when we populate the pcommon data structure. 2. Adds unit tests for including `container.image.repo_digests` in rules for extraction. 3. Includes `container.image.repo_digests` in the e2e tests for k8sattributes processor. **Documentation:** Added `container.image.repo_digests` as an example of attributes that can be extracted from a container --------- Co-authored-by: Christos Markou <chrismarkou92@gmail.com> --- .chloggen/container-repo-digests.yaml | 27 + processor/k8sattributesprocessor/README.md | 2 + processor/k8sattributesprocessor/config.go | 6 +- .../k8sattributesprocessor/documentation.md | 1 + processor/k8sattributesprocessor/e2e_test.go | 743 +++++++++--------- processor/k8sattributesprocessor/go.mod | 2 +- .../internal/kube/client.go | 47 +- .../internal/kube/client_test.go | 73 +- .../internal/kube/kube.go | 52 +- .../internal/metadata/generated_config.go | 52 +- .../metadata/generated_config_test.go | 98 +-- .../internal/metadata/generated_resource.go | 7 + .../metadata/generated_resource_test.go | 8 +- .../internal/metadata/testdata/config.yaml | 4 + .../k8sattributesprocessor/metadata.yaml | 4 + processor/k8sattributesprocessor/options.go | 13 +- processor/k8sattributesprocessor/processor.go | 7 +- .../k8sattributesprocessor/processor_test.go | 62 +- .../e2e/clusterrbac/collector/configmap.yaml | 1 + .../e2e/mixrbac/collector/configmap.yaml | 1 + .../namespacedrbac/collector/configmap.yaml | 1 + 21 files changed, 708 insertions(+), 503 deletions(-) create mode 100644 .chloggen/container-repo-digests.yaml diff --git a/.chloggen/container-repo-digests.yaml b/.chloggen/container-repo-digests.yaml new file mode 100644 index 000000000000..4884dde3a881 --- /dev/null +++ b/.chloggen/container-repo-digests.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: processor/k8sattributes + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add support for `container.image.repo_digests` metadata + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [34029] + +# (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] diff --git a/processor/k8sattributesprocessor/README.md b/processor/k8sattributesprocessor/README.md index ee6e4ac8e0cb..3a01daab2c5a 100644 --- a/processor/k8sattributesprocessor/README.md +++ b/processor/k8sattributesprocessor/README.md @@ -75,9 +75,11 @@ Additional container level attributes can be extracted provided that certain res - k8s.container.name - container.image.name - container.image.tag + - container.image.repo_digests (if k8s CRI populates [repository digest field](https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/model/registry/container.yaml#L60-L71)) 2. If the `k8s.container.name` resource attribute is provided, the following additional attributes will be available: - container.image.name - container.image.tag + - container.image.repo_digests (if k8s CRI populates [repository digest field](https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/model/registry/container.yaml#L60-L71)) 3. If the `k8s.container.restart_count` resource attribute is provided, it can be used to associate with a particular container instance. If it's not set, the latest container instance will be used: - container.id (not added by default, has to be specified in `metadata`) diff --git a/processor/k8sattributesprocessor/config.go b/processor/k8sattributesprocessor/config.go index d2cca583416b..27b49cef5d63 100644 --- a/processor/k8sattributesprocessor/config.go +++ b/processor/k8sattributesprocessor/config.go @@ -105,7 +105,7 @@ func (cfg *Config) Validate() error { conventions.AttributeK8SNodeName, conventions.AttributeK8SNodeUID, conventions.AttributeK8SContainerName, conventions.AttributeContainerID, conventions.AttributeContainerImageName, conventions.AttributeContainerImageTag, - clusterUID: + containerImageRepoDigests, clusterUID: default: return fmt.Errorf("\"%s\" is not a supported metadata field", field) } @@ -143,8 +143,8 @@ type ExtractConfig struct { // k8s.daemonset.name, k8s.daemonset.uid, // k8s.job.name, k8s.job.uid, k8s.cronjob.name, // k8s.statefulset.name, k8s.statefulset.uid, - // k8s.container.name, container.image.name, - // container.image.tag, container.id + // k8s.container.name, container.id, container.image.name, + // container.image.tag, container.image.repo_digests // k8s.cluster.uid // // Specifying anything other than these values will result in an error. diff --git a/processor/k8sattributesprocessor/documentation.md b/processor/k8sattributesprocessor/documentation.md index fc86fa8010af..67bcb77d2741 100644 --- a/processor/k8sattributesprocessor/documentation.md +++ b/processor/k8sattributesprocessor/documentation.md @@ -8,6 +8,7 @@ | ---- | ----------- | ------ | ------- | | container.id | Container ID. Usually a UUID, as for example used to identify Docker containers. The UUID might be abbreviated. Requires k8s.container.restart_count. | Any Str | false | | container.image.name | Name of the image the container was built on. Requires container.id or k8s.container.name. | Any Str | true | +| container.image.repo_digests | Repo digests of the container image as provided by the container runtime. | Any Slice | false | | container.image.tag | Container image tag. Requires container.id or k8s.container.name. | Any Str | true | | k8s.cluster.uid | Gives cluster uid identified with kube-system namespace | Any Str | false | | k8s.container.name | The name of the Container in a Pod template. Requires container.id. | Any Str | false | diff --git a/processor/k8sattributesprocessor/e2e_test.go b/processor/k8sattributesprocessor/e2e_test.go index 4800516655b2..ef0b553ca320 100644 --- a/processor/k8sattributesprocessor/e2e_test.go +++ b/processor/k8sattributesprocessor/e2e_test.go @@ -116,23 +116,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-job", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-job-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), - "k8s.job.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "job"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-job-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), + "k8s.job.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "job"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -140,23 +141,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-statefulset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset-0"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), - "k8s.statefulset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset-0"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), + "k8s.statefulset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -164,24 +166,25 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.deployment.uid": newExpectedValue(exist, ""), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.deployment.uid": newExpectedValue(exist, ""), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -189,23 +192,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-daemonset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-daemonset-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), - "k8s.daemonset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-daemonset-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), + "k8s.daemonset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -213,23 +217,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-job", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-job-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), - "k8s.job.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "job"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-job-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), + "k8s.job.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "job"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -237,23 +242,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-statefulset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset-0"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), - "k8s.statefulset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset-0"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), + "k8s.statefulset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -261,24 +267,25 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.deployment.uid": newExpectedValue(exist, ""), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.deployment.uid": newExpectedValue(exist, ""), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -286,23 +293,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-daemonset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-daemonset-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), - "k8s.daemonset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-daemonset-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), + "k8s.daemonset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -310,23 +318,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-job", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-job-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), - "k8s.job.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "job"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-job-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), + "k8s.job.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "job"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -334,22 +343,23 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-statefulset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset-0"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), - "k8s.statefulset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset-0"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), + "k8s.statefulset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -357,25 +367,26 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.deployment.uid": newExpectedValue(exist, ""), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.deployment.uid": newExpectedValue(exist, ""), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -383,23 +394,24 @@ func TestE2E_ClusterRBAC(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-daemonset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-daemonset-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, testNs), - "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), - "k8s.daemonset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-daemonset-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), + "k8s.daemonset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, } @@ -477,22 +489,23 @@ func TestE2E_NamespacedRBAC(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, nsName), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), }, }, { @@ -500,22 +513,23 @@ func TestE2E_NamespacedRBAC(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, nsName), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), }, }, { @@ -523,22 +537,23 @@ func TestE2E_NamespacedRBAC(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, nsName), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), }, }, } @@ -632,25 +647,26 @@ func TestE2E_MixRBAC(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, workloadNs), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, workloadNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), }, }, { @@ -658,25 +674,26 @@ func TestE2E_MixRBAC(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, workloadNs), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, workloadNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), }, }, { @@ -684,25 +701,26 @@ func TestE2E_MixRBAC(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, workloadNs), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), - "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, workloadNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(regex, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen@sha256:[0-9a-fA-f]{64}"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), }, }, } @@ -726,6 +744,8 @@ func TestE2E_MixRBAC(t *testing.T) { // Test with `filter::namespace` set and only role binding to collector's SA. We can't get node and namespace labels/annotations. // While `k8s.pod.ip` is not set in `k8sattributes:extract:metadata` and the `pod_association` is not `connection` // we expect that the `k8s.pod.ip` metadata is not added. +// While `container.image.repo_digests` is not set in `k8sattributes::extract::metadata`, we expect +// that the `container.image.repo_digests` metadata is not added func TestE2E_NamespacedRBACNoPodIP(t *testing.T) { testDir := filepath.Join("testdata", "e2e", "namespaced_rbac_no_pod_ip") @@ -781,22 +801,23 @@ func TestE2E_NamespacedRBACNoPodIP(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(shouldnotexist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, nsName), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(shouldnotexist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(shouldnotexist, ""), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), }, }, { @@ -804,22 +825,23 @@ func TestE2E_NamespacedRBACNoPodIP(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(shouldnotexist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, nsName), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(shouldnotexist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(shouldnotexist, ""), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), }, }, { @@ -827,22 +849,23 @@ func TestE2E_NamespacedRBACNoPodIP(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(shouldnotexist, ""), - "k8s.pod.uid": newExpectedValue(regex, uidRe), - "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, nsName), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.deployment.uid": newExpectedValue(regex, uidRe), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(regex, uidRe), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(shouldnotexist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.repo_digests": newExpectedValue(shouldnotexist, ""), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), }, }, } diff --git a/processor/k8sattributesprocessor/go.mod b/processor/k8sattributesprocessor/go.mod index 80037af7bc5b..764c3ef71662 100644 --- a/processor/k8sattributesprocessor/go.mod +++ b/processor/k8sattributesprocessor/go.mod @@ -3,6 +3,7 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sat go 1.21.0 require ( + github.com/distribution/reference v0.5.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.106.1 @@ -38,7 +39,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/distribution/reference v0.5.0 // indirect github.com/docker/docker v26.1.5+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect diff --git a/processor/k8sattributesprocessor/internal/kube/client.go b/processor/k8sattributesprocessor/internal/kube/client.go index 9270af014b4d..a43049f09ebf 100644 --- a/processor/k8sattributesprocessor/internal/kube/client.go +++ b/processor/k8sattributesprocessor/internal/kube/client.go @@ -11,6 +11,7 @@ import ( "sync" "time" + "github.com/distribution/reference" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/featuregate" conventions "go.opentelemetry.io/collector/semconv/v1.6.1" @@ -572,24 +573,26 @@ func removeUnnecessaryPodData(pod *api_v1.Pod, rules ExtractionRules) *api_v1.Po } if needContainerAttributes(rules) { + removeUnnecessaryContainerStatus := func(c api_v1.ContainerStatus) api_v1.ContainerStatus { + transformedContainerStatus := api_v1.ContainerStatus{ + Name: c.Name, + ContainerID: c.ContainerID, + RestartCount: c.RestartCount, + } + if rules.ContainerImageRepoDigests { + transformedContainerStatus.ImageID = c.ImageID + } + return transformedContainerStatus + } + for _, containerStatus := range pod.Status.ContainerStatuses { transformedPod.Status.ContainerStatuses = append( - transformedPod.Status.ContainerStatuses, - api_v1.ContainerStatus{ - Name: containerStatus.Name, - ContainerID: containerStatus.ContainerID, - RestartCount: containerStatus.RestartCount, - }, + transformedPod.Status.ContainerStatuses, removeUnnecessaryContainerStatus(containerStatus), ) } for _, containerStatus := range pod.Status.InitContainerStatuses { transformedPod.Status.InitContainerStatuses = append( - transformedPod.Status.InitContainerStatuses, - api_v1.ContainerStatus{ - Name: containerStatus.Name, - ContainerID: containerStatus.ContainerID, - RestartCount: containerStatus.RestartCount, - }, + transformedPod.Status.InitContainerStatuses, removeUnnecessaryContainerStatus(containerStatus), ) } @@ -670,11 +673,26 @@ func (c *WatchClient) extractPodContainersAttributes(pod *api_v1.Pod) PodContain containerID = parts[1] } containers.ByID[containerID] = container - if c.Rules.ContainerID { + if c.Rules.ContainerID || c.Rules.ContainerImageRepoDigests { if container.Statuses == nil { container.Statuses = map[int]ContainerStatus{} } - container.Statuses[int(apiStatus.RestartCount)] = ContainerStatus{containerID} + containerStatus := ContainerStatus{} + if c.Rules.ContainerID { + containerStatus.ContainerID = containerID + } + + if c.Rules.ContainerImageRepoDigests { + if parsed, err := reference.ParseAnyReference(apiStatus.ImageID); err == nil { + switch parsed.(type) { + case reference.Canonical: + containerStatus.ImageRepoDigest = parsed.String() + default: + } + } + } + + container.Statuses[int(apiStatus.RestartCount)] = containerStatus } } return containers @@ -965,6 +983,7 @@ func needContainerAttributes(rules ExtractionRules) bool { return rules.ContainerImageName || rules.ContainerName || rules.ContainerImageTag || + rules.ContainerImageRepoDigests || rules.ContainerID } diff --git a/processor/k8sattributesprocessor/internal/kube/client_test.go b/processor/k8sattributesprocessor/internal/kube/client_test.go index 0c314c25d2ef..de6f1dace5d6 100644 --- a/processor/k8sattributesprocessor/internal/kube/client_test.go +++ b/processor/k8sattributesprocessor/internal/kube/client_test.go @@ -1488,18 +1488,21 @@ func Test_extractPodContainersAttributes(t *testing.T) { { Name: "container1", ContainerID: "docker://container1-id-123", + ImageID: "docker.io/otel/collector@sha256:55d008bc28344c3178645d40e7d07df30f9d90abe4b53c3fc4e5e9c0295533da", RestartCount: 0, }, { Name: "container2", ContainerID: "docker://container2-id-456", + ImageID: "sha256:430ac608abaa332de4ce45d68534447c7a206edc5e98aaff9923ecc12f8a80d9", RestartCount: 2, }, }, InitContainerStatuses: []api_v1.ContainerStatus{ { Name: "init_container", - ContainerID: "containerd://init-container-id-123", + ContainerID: "containerd://init-container-id-789", + ImageID: "ghcr.io/initimage1@sha256:42e8ba40f9f70d604684c3a2a0ed321206b7e2e3509fdb2c8836d34f2edfb57b", RestartCount: 0, }, }, @@ -1537,7 +1540,7 @@ func Test_extractPodContainersAttributes(t *testing.T) { ByID: map[string]*Container{ "container1-id-123": {ImageName: "test/image1"}, "container2-id-456": {ImageName: "example.com:port1/image2"}, - "init-container-id-123": {ImageName: "test/init-image"}, + "init-container-id-789": {ImageName: "test/init-image"}, }, ByName: map[string]*Container{ "container1": {ImageName: "test/image1"}, @@ -1586,9 +1589,9 @@ func Test_extractPodContainersAttributes(t *testing.T) { 2: {ContainerID: "container2-id-456"}, }, }, - "init-container-id-123": { + "init-container-id-789": { Statuses: map[int]ContainerStatus{ - 0: {ContainerID: "init-container-id-123"}, + 0: {ContainerID: "init-container-id-789"}, }, }, }, @@ -1605,7 +1608,50 @@ func Test_extractPodContainersAttributes(t *testing.T) { }, "init_container": { Statuses: map[int]ContainerStatus{ - 0: {ContainerID: "init-container-id-123"}, + 0: {ContainerID: "init-container-id-789"}, + }, + }, + }, + }, + }, + { + name: "container-image-repo-digest-only", + rules: ExtractionRules{ + ContainerImageRepoDigests: true, + }, + pod: &pod, + want: PodContainers{ + ByID: map[string]*Container{ + "container1-id-123": { + Statuses: map[int]ContainerStatus{ + 0: {ImageRepoDigest: "docker.io/otel/collector@sha256:55d008bc28344c3178645d40e7d07df30f9d90abe4b53c3fc4e5e9c0295533da"}, + }, + }, + "container2-id-456": { + Statuses: map[int]ContainerStatus{ + 2: {}, + }, + }, + "init-container-id-789": { + Statuses: map[int]ContainerStatus{ + 0: {ImageRepoDigest: "ghcr.io/initimage1@sha256:42e8ba40f9f70d604684c3a2a0ed321206b7e2e3509fdb2c8836d34f2edfb57b"}, + }, + }, + }, + ByName: map[string]*Container{ + "container1": { + Statuses: map[int]ContainerStatus{ + 0: {ImageRepoDigest: "docker.io/otel/collector@sha256:55d008bc28344c3178645d40e7d07df30f9d90abe4b53c3fc4e5e9c0295533da"}, + }, + }, + "container2": { + Statuses: map[int]ContainerStatus{ + 2: {}, + }, + }, + "init_container": { + Statuses: map[int]ContainerStatus{ + 0: {ImageRepoDigest: "ghcr.io/initimage1@sha256:42e8ba40f9f70d604684c3a2a0ed321206b7e2e3509fdb2c8836d34f2edfb57b"}, }, }, }, @@ -1614,9 +1660,10 @@ func Test_extractPodContainersAttributes(t *testing.T) { { name: "all-container-attributes", rules: ExtractionRules{ - ContainerImageName: true, - ContainerImageTag: true, - ContainerID: true, + ContainerImageName: true, + ContainerImageTag: true, + ContainerID: true, + ContainerImageRepoDigests: true, }, pod: &pod, want: PodContainers{ @@ -1625,7 +1672,7 @@ func Test_extractPodContainersAttributes(t *testing.T) { ImageName: "test/image1", ImageTag: "0.1.0", Statuses: map[int]ContainerStatus{ - 0: {ContainerID: "container1-id-123"}, + 0: {ContainerID: "container1-id-123", ImageRepoDigest: "docker.io/otel/collector@sha256:55d008bc28344c3178645d40e7d07df30f9d90abe4b53c3fc4e5e9c0295533da"}, }, }, "container2-id-456": { @@ -1635,11 +1682,11 @@ func Test_extractPodContainersAttributes(t *testing.T) { 2: {ContainerID: "container2-id-456"}, }, }, - "init-container-id-123": { + "init-container-id-789": { ImageName: "test/init-image", ImageTag: "1.0.2", Statuses: map[int]ContainerStatus{ - 0: {ContainerID: "init-container-id-123"}, + 0: {ContainerID: "init-container-id-789", ImageRepoDigest: "ghcr.io/initimage1@sha256:42e8ba40f9f70d604684c3a2a0ed321206b7e2e3509fdb2c8836d34f2edfb57b"}, }, }, }, @@ -1648,7 +1695,7 @@ func Test_extractPodContainersAttributes(t *testing.T) { ImageName: "test/image1", ImageTag: "0.1.0", Statuses: map[int]ContainerStatus{ - 0: {ContainerID: "container1-id-123"}, + 0: {ContainerID: "container1-id-123", ImageRepoDigest: "docker.io/otel/collector@sha256:55d008bc28344c3178645d40e7d07df30f9d90abe4b53c3fc4e5e9c0295533da"}, }, }, "container2": { @@ -1662,7 +1709,7 @@ func Test_extractPodContainersAttributes(t *testing.T) { ImageName: "test/init-image", ImageTag: "1.0.2", Statuses: map[int]ContainerStatus{ - 0: {ContainerID: "init-container-id-123"}, + 0: {ContainerID: "init-container-id-789", ImageRepoDigest: "ghcr.io/initimage1@sha256:42e8ba40f9f70d604684c3a2a0ed321206b7e2e3509fdb2c8836d34f2edfb57b"}, }, }, }, diff --git a/processor/k8sattributesprocessor/internal/kube/kube.go b/processor/k8sattributesprocessor/internal/kube/kube.go index 9e3f4835a92b..6145cb972235 100644 --- a/processor/k8sattributesprocessor/internal/kube/kube.go +++ b/processor/k8sattributesprocessor/internal/kube/kube.go @@ -140,7 +140,8 @@ type Container struct { // ContainerStatus stores resource attributes for a particular container run defined by k8s pod status. type ContainerStatus struct { - ContainerID string + ContainerID string + ImageRepoDigest string } // Namespace represents a kubernetes namespace. @@ -194,30 +195,31 @@ type FieldFilter struct { // ExtractionRules is used to specify the information that needs to be extracted // from pods and added to the spans as tags. type ExtractionRules struct { - CronJobName bool - DeploymentName bool - DeploymentUID bool - DaemonSetUID bool - DaemonSetName bool - JobUID bool - JobName bool - Namespace bool - PodName bool - PodUID bool - PodHostName bool - PodIP bool - ReplicaSetID bool - ReplicaSetName bool - StatefulSetUID bool - StatefulSetName bool - Node bool - NodeUID bool - StartTime bool - ContainerName bool - ContainerID bool - ContainerImageName bool - ContainerImageTag bool - ClusterUID bool + CronJobName bool + DeploymentName bool + DeploymentUID bool + DaemonSetUID bool + DaemonSetName bool + JobUID bool + JobName bool + Namespace bool + PodName bool + PodUID bool + PodHostName bool + PodIP bool + ReplicaSetID bool + ReplicaSetName bool + StatefulSetUID bool + StatefulSetName bool + Node bool + NodeUID bool + StartTime bool + ContainerName bool + ContainerID bool + ContainerImageName bool + ContainerImageRepoDigests bool + ContainerImageTag bool + ClusterUID bool Annotations []FieldExtractionRule Labels []FieldExtractionRule diff --git a/processor/k8sattributesprocessor/internal/metadata/generated_config.go b/processor/k8sattributesprocessor/internal/metadata/generated_config.go index 98844fe00f8e..aa6c1caa9f5c 100644 --- a/processor/k8sattributesprocessor/internal/metadata/generated_config.go +++ b/processor/k8sattributesprocessor/internal/metadata/generated_config.go @@ -27,30 +27,31 @@ func (rac *ResourceAttributeConfig) Unmarshal(parser *confmap.Conf) error { // ResourceAttributesConfig provides config for k8sattributes resource attributes. type ResourceAttributesConfig struct { - ContainerID ResourceAttributeConfig `mapstructure:"container.id"` - ContainerImageName ResourceAttributeConfig `mapstructure:"container.image.name"` - ContainerImageTag ResourceAttributeConfig `mapstructure:"container.image.tag"` - K8sClusterUID ResourceAttributeConfig `mapstructure:"k8s.cluster.uid"` - K8sContainerName ResourceAttributeConfig `mapstructure:"k8s.container.name"` - K8sCronjobName ResourceAttributeConfig `mapstructure:"k8s.cronjob.name"` - K8sDaemonsetName ResourceAttributeConfig `mapstructure:"k8s.daemonset.name"` - K8sDaemonsetUID ResourceAttributeConfig `mapstructure:"k8s.daemonset.uid"` - K8sDeploymentName ResourceAttributeConfig `mapstructure:"k8s.deployment.name"` - K8sDeploymentUID ResourceAttributeConfig `mapstructure:"k8s.deployment.uid"` - K8sJobName ResourceAttributeConfig `mapstructure:"k8s.job.name"` - K8sJobUID ResourceAttributeConfig `mapstructure:"k8s.job.uid"` - K8sNamespaceName ResourceAttributeConfig `mapstructure:"k8s.namespace.name"` - K8sNodeName ResourceAttributeConfig `mapstructure:"k8s.node.name"` - K8sNodeUID ResourceAttributeConfig `mapstructure:"k8s.node.uid"` - K8sPodHostname ResourceAttributeConfig `mapstructure:"k8s.pod.hostname"` - K8sPodIP ResourceAttributeConfig `mapstructure:"k8s.pod.ip"` - K8sPodName ResourceAttributeConfig `mapstructure:"k8s.pod.name"` - K8sPodStartTime ResourceAttributeConfig `mapstructure:"k8s.pod.start_time"` - K8sPodUID ResourceAttributeConfig `mapstructure:"k8s.pod.uid"` - K8sReplicasetName ResourceAttributeConfig `mapstructure:"k8s.replicaset.name"` - K8sReplicasetUID ResourceAttributeConfig `mapstructure:"k8s.replicaset.uid"` - K8sStatefulsetName ResourceAttributeConfig `mapstructure:"k8s.statefulset.name"` - K8sStatefulsetUID ResourceAttributeConfig `mapstructure:"k8s.statefulset.uid"` + ContainerID ResourceAttributeConfig `mapstructure:"container.id"` + ContainerImageName ResourceAttributeConfig `mapstructure:"container.image.name"` + ContainerImageRepoDigests ResourceAttributeConfig `mapstructure:"container.image.repo_digests"` + ContainerImageTag ResourceAttributeConfig `mapstructure:"container.image.tag"` + K8sClusterUID ResourceAttributeConfig `mapstructure:"k8s.cluster.uid"` + K8sContainerName ResourceAttributeConfig `mapstructure:"k8s.container.name"` + K8sCronjobName ResourceAttributeConfig `mapstructure:"k8s.cronjob.name"` + K8sDaemonsetName ResourceAttributeConfig `mapstructure:"k8s.daemonset.name"` + K8sDaemonsetUID ResourceAttributeConfig `mapstructure:"k8s.daemonset.uid"` + K8sDeploymentName ResourceAttributeConfig `mapstructure:"k8s.deployment.name"` + K8sDeploymentUID ResourceAttributeConfig `mapstructure:"k8s.deployment.uid"` + K8sJobName ResourceAttributeConfig `mapstructure:"k8s.job.name"` + K8sJobUID ResourceAttributeConfig `mapstructure:"k8s.job.uid"` + K8sNamespaceName ResourceAttributeConfig `mapstructure:"k8s.namespace.name"` + K8sNodeName ResourceAttributeConfig `mapstructure:"k8s.node.name"` + K8sNodeUID ResourceAttributeConfig `mapstructure:"k8s.node.uid"` + K8sPodHostname ResourceAttributeConfig `mapstructure:"k8s.pod.hostname"` + K8sPodIP ResourceAttributeConfig `mapstructure:"k8s.pod.ip"` + K8sPodName ResourceAttributeConfig `mapstructure:"k8s.pod.name"` + K8sPodStartTime ResourceAttributeConfig `mapstructure:"k8s.pod.start_time"` + K8sPodUID ResourceAttributeConfig `mapstructure:"k8s.pod.uid"` + K8sReplicasetName ResourceAttributeConfig `mapstructure:"k8s.replicaset.name"` + K8sReplicasetUID ResourceAttributeConfig `mapstructure:"k8s.replicaset.uid"` + K8sStatefulsetName ResourceAttributeConfig `mapstructure:"k8s.statefulset.name"` + K8sStatefulsetUID ResourceAttributeConfig `mapstructure:"k8s.statefulset.uid"` } func DefaultResourceAttributesConfig() ResourceAttributesConfig { @@ -61,6 +62,9 @@ func DefaultResourceAttributesConfig() ResourceAttributesConfig { ContainerImageName: ResourceAttributeConfig{ Enabled: true, }, + ContainerImageRepoDigests: ResourceAttributeConfig{ + Enabled: false, + }, ContainerImageTag: ResourceAttributeConfig{ Enabled: true, }, diff --git a/processor/k8sattributesprocessor/internal/metadata/generated_config_test.go b/processor/k8sattributesprocessor/internal/metadata/generated_config_test.go index 2002ac62a2e4..ea74fbaaafb7 100644 --- a/processor/k8sattributesprocessor/internal/metadata/generated_config_test.go +++ b/processor/k8sattributesprocessor/internal/metadata/generated_config_test.go @@ -24,59 +24,61 @@ func TestResourceAttributesConfig(t *testing.T) { { name: "all_set", want: ResourceAttributesConfig{ - ContainerID: ResourceAttributeConfig{Enabled: true}, - ContainerImageName: ResourceAttributeConfig{Enabled: true}, - ContainerImageTag: ResourceAttributeConfig{Enabled: true}, - K8sClusterUID: ResourceAttributeConfig{Enabled: true}, - K8sContainerName: ResourceAttributeConfig{Enabled: true}, - K8sCronjobName: ResourceAttributeConfig{Enabled: true}, - K8sDaemonsetName: ResourceAttributeConfig{Enabled: true}, - K8sDaemonsetUID: ResourceAttributeConfig{Enabled: true}, - K8sDeploymentName: ResourceAttributeConfig{Enabled: true}, - K8sDeploymentUID: ResourceAttributeConfig{Enabled: true}, - K8sJobName: ResourceAttributeConfig{Enabled: true}, - K8sJobUID: ResourceAttributeConfig{Enabled: true}, - K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, - K8sNodeName: ResourceAttributeConfig{Enabled: true}, - K8sNodeUID: ResourceAttributeConfig{Enabled: true}, - K8sPodHostname: ResourceAttributeConfig{Enabled: true}, - K8sPodIP: ResourceAttributeConfig{Enabled: true}, - K8sPodName: ResourceAttributeConfig{Enabled: true}, - K8sPodStartTime: ResourceAttributeConfig{Enabled: true}, - K8sPodUID: ResourceAttributeConfig{Enabled: true}, - K8sReplicasetName: ResourceAttributeConfig{Enabled: true}, - K8sReplicasetUID: ResourceAttributeConfig{Enabled: true}, - K8sStatefulsetName: ResourceAttributeConfig{Enabled: true}, - K8sStatefulsetUID: ResourceAttributeConfig{Enabled: true}, + ContainerID: ResourceAttributeConfig{Enabled: true}, + ContainerImageName: ResourceAttributeConfig{Enabled: true}, + ContainerImageRepoDigests: ResourceAttributeConfig{Enabled: true}, + ContainerImageTag: ResourceAttributeConfig{Enabled: true}, + K8sClusterUID: ResourceAttributeConfig{Enabled: true}, + K8sContainerName: ResourceAttributeConfig{Enabled: true}, + K8sCronjobName: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: true}, + K8sJobName: ResourceAttributeConfig{Enabled: true}, + K8sJobUID: ResourceAttributeConfig{Enabled: true}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, + K8sNodeName: ResourceAttributeConfig{Enabled: true}, + K8sNodeUID: ResourceAttributeConfig{Enabled: true}, + K8sPodHostname: ResourceAttributeConfig{Enabled: true}, + K8sPodIP: ResourceAttributeConfig{Enabled: true}, + K8sPodName: ResourceAttributeConfig{Enabled: true}, + K8sPodStartTime: ResourceAttributeConfig{Enabled: true}, + K8sPodUID: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: true}, }, }, { name: "none_set", want: ResourceAttributesConfig{ - ContainerID: ResourceAttributeConfig{Enabled: false}, - ContainerImageName: ResourceAttributeConfig{Enabled: false}, - ContainerImageTag: ResourceAttributeConfig{Enabled: false}, - K8sClusterUID: ResourceAttributeConfig{Enabled: false}, - K8sContainerName: ResourceAttributeConfig{Enabled: false}, - K8sCronjobName: ResourceAttributeConfig{Enabled: false}, - K8sDaemonsetName: ResourceAttributeConfig{Enabled: false}, - K8sDaemonsetUID: ResourceAttributeConfig{Enabled: false}, - K8sDeploymentName: ResourceAttributeConfig{Enabled: false}, - K8sDeploymentUID: ResourceAttributeConfig{Enabled: false}, - K8sJobName: ResourceAttributeConfig{Enabled: false}, - K8sJobUID: ResourceAttributeConfig{Enabled: false}, - K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, - K8sNodeName: ResourceAttributeConfig{Enabled: false}, - K8sNodeUID: ResourceAttributeConfig{Enabled: false}, - K8sPodHostname: ResourceAttributeConfig{Enabled: false}, - K8sPodIP: ResourceAttributeConfig{Enabled: false}, - K8sPodName: ResourceAttributeConfig{Enabled: false}, - K8sPodStartTime: ResourceAttributeConfig{Enabled: false}, - K8sPodUID: ResourceAttributeConfig{Enabled: false}, - K8sReplicasetName: ResourceAttributeConfig{Enabled: false}, - K8sReplicasetUID: ResourceAttributeConfig{Enabled: false}, - K8sStatefulsetName: ResourceAttributeConfig{Enabled: false}, - K8sStatefulsetUID: ResourceAttributeConfig{Enabled: false}, + ContainerID: ResourceAttributeConfig{Enabled: false}, + ContainerImageName: ResourceAttributeConfig{Enabled: false}, + ContainerImageRepoDigests: ResourceAttributeConfig{Enabled: false}, + ContainerImageTag: ResourceAttributeConfig{Enabled: false}, + K8sClusterUID: ResourceAttributeConfig{Enabled: false}, + K8sContainerName: ResourceAttributeConfig{Enabled: false}, + K8sCronjobName: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: false}, + K8sJobName: ResourceAttributeConfig{Enabled: false}, + K8sJobUID: ResourceAttributeConfig{Enabled: false}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, + K8sNodeName: ResourceAttributeConfig{Enabled: false}, + K8sNodeUID: ResourceAttributeConfig{Enabled: false}, + K8sPodHostname: ResourceAttributeConfig{Enabled: false}, + K8sPodIP: ResourceAttributeConfig{Enabled: false}, + K8sPodName: ResourceAttributeConfig{Enabled: false}, + K8sPodStartTime: ResourceAttributeConfig{Enabled: false}, + K8sPodUID: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: false}, }, }, } diff --git a/processor/k8sattributesprocessor/internal/metadata/generated_resource.go b/processor/k8sattributesprocessor/internal/metadata/generated_resource.go index 398a0a38131e..6545b2c918c1 100644 --- a/processor/k8sattributesprocessor/internal/metadata/generated_resource.go +++ b/processor/k8sattributesprocessor/internal/metadata/generated_resource.go @@ -35,6 +35,13 @@ func (rb *ResourceBuilder) SetContainerImageName(val string) { } } +// SetContainerImageRepoDigests sets provided value as "container.image.repo_digests" attribute. +func (rb *ResourceBuilder) SetContainerImageRepoDigests(val []any) { + if rb.config.ContainerImageRepoDigests.Enabled { + rb.res.Attributes().PutEmptySlice("container.image.repo_digests").FromRaw(val) + } +} + // SetContainerImageTag sets provided value as "container.image.tag" attribute. func (rb *ResourceBuilder) SetContainerImageTag(val string) { if rb.config.ContainerImageTag.Enabled { diff --git a/processor/k8sattributesprocessor/internal/metadata/generated_resource_test.go b/processor/k8sattributesprocessor/internal/metadata/generated_resource_test.go index ed75e6522145..02486fcc796a 100644 --- a/processor/k8sattributesprocessor/internal/metadata/generated_resource_test.go +++ b/processor/k8sattributesprocessor/internal/metadata/generated_resource_test.go @@ -15,6 +15,7 @@ func TestResourceBuilder(t *testing.T) { rb := NewResourceBuilder(cfg) rb.SetContainerID("container.id-val") rb.SetContainerImageName("container.image.name-val") + rb.SetContainerImageRepoDigests([]any{"container.image.repo_digests-item1", "container.image.repo_digests-item2"}) rb.SetContainerImageTag("container.image.tag-val") rb.SetK8sClusterUID("k8s.cluster.uid-val") rb.SetK8sContainerName("k8s.container.name-val") @@ -45,7 +46,7 @@ func TestResourceBuilder(t *testing.T) { case "default": assert.Equal(t, 8, res.Attributes().Len()) case "all_set": - assert.Equal(t, 24, res.Attributes().Len()) + assert.Equal(t, 25, res.Attributes().Len()) case "none_set": assert.Equal(t, 0, res.Attributes().Len()) return @@ -63,6 +64,11 @@ func TestResourceBuilder(t *testing.T) { if ok { assert.EqualValues(t, "container.image.name-val", val.Str()) } + val, ok = res.Attributes().Get("container.image.repo_digests") + assert.Equal(t, test == "all_set", ok) + if ok { + assert.EqualValues(t, []any{"container.image.repo_digests-item1", "container.image.repo_digests-item2"}, val.Slice().AsRaw()) + } val, ok = res.Attributes().Get("container.image.tag") assert.True(t, ok) if ok { diff --git a/processor/k8sattributesprocessor/internal/metadata/testdata/config.yaml b/processor/k8sattributesprocessor/internal/metadata/testdata/config.yaml index e5a0495e1451..d2fee538b731 100644 --- a/processor/k8sattributesprocessor/internal/metadata/testdata/config.yaml +++ b/processor/k8sattributesprocessor/internal/metadata/testdata/config.yaml @@ -5,6 +5,8 @@ all_set: enabled: true container.image.name: enabled: true + container.image.repo_digests: + enabled: true container.image.tag: enabled: true k8s.cluster.uid: @@ -55,6 +57,8 @@ none_set: enabled: false container.image.name: enabled: false + container.image.repo_digests: + enabled: false container.image.tag: enabled: false k8s.cluster.uid: diff --git a/processor/k8sattributesprocessor/metadata.yaml b/processor/k8sattributesprocessor/metadata.yaml index ae5728c9a7d2..f9f02fcd9814 100644 --- a/processor/k8sattributesprocessor/metadata.yaml +++ b/processor/k8sattributesprocessor/metadata.yaml @@ -102,6 +102,10 @@ resource_attributes: description: Name of the image the container was built on. Requires container.id or k8s.container.name. type: string enabled: true + container.image.repo_digests: + description: Repo digests of the container image as provided by the container runtime. + type: slice + enabled: false container.image.tag: description: Container image tag. Requires container.id or k8s.container.name. type: string diff --git a/processor/k8sattributesprocessor/options.go b/processor/k8sattributesprocessor/options.go index c28ff303a989..ec39cdb9b827 100644 --- a/processor/k8sattributesprocessor/options.go +++ b/processor/k8sattributesprocessor/options.go @@ -24,8 +24,12 @@ const ( metadataPodIP = "k8s.pod.ip" metadataPodStartTime = "k8s.pod.start_time" specPodHostName = "k8s.pod.hostname" - // TODO: use k8s.cluster.uid from semconv when available, and replace clusterUID with conventions.AttributeClusterUid - clusterUID = "k8s.cluster.uid" + // TODO: use k8s.cluster.uid, container.image.repo_digests + // from semconv when available, + // replace clusterUID with conventions.AttributeK8SClusterUID + // replace containerRepoDigests with conventions.AttributeContainerImageRepoDigests + clusterUID = "k8s.cluster.uid" + containerImageRepoDigests = "container.image.repo_digests" ) // option represents a configuration option that can be passes. @@ -62,6 +66,9 @@ func enabledAttributes() (attributes []string) { if defaultConfig.ContainerImageName.Enabled { attributes = append(attributes, conventions.AttributeContainerImageName) } + if defaultConfig.ContainerImageRepoDigests.Enabled { + attributes = append(attributes, containerImageRepoDigests) + } if defaultConfig.ContainerImageTag.Enabled { attributes = append(attributes, conventions.AttributeContainerImageTag) } @@ -178,6 +185,8 @@ func withExtractMetadata(fields ...string) option { p.rules.ContainerID = true case conventions.AttributeContainerImageName: p.rules.ContainerImageName = true + case containerImageRepoDigests: + p.rules.ContainerImageRepoDigests = true case conventions.AttributeContainerImageTag: p.rules.ContainerImageTag = true case clusterUID: diff --git a/processor/k8sattributesprocessor/processor.go b/processor/k8sattributesprocessor/processor.go index b77b02b0407c..669006690a3b 100644 --- a/processor/k8sattributesprocessor/processor.go +++ b/processor/k8sattributesprocessor/processor.go @@ -245,10 +245,13 @@ func (kp *kubernetesprocessor) addContainerAttributes(attrs pcommon.Map, pod *ku } } if runID != -1 { - if containerStatus, ok := containerSpec.Statuses[runID]; ok && containerStatus.ContainerID != "" { - if _, found := attrs.Get(conventions.AttributeContainerID); !found { + if containerStatus, ok := containerSpec.Statuses[runID]; ok { + if _, found := attrs.Get(conventions.AttributeContainerID); !found && containerStatus.ContainerID != "" { attrs.PutStr(conventions.AttributeContainerID, containerStatus.ContainerID) } + if _, found := attrs.Get(containerImageRepoDigests); !found && containerStatus.ImageRepoDigest != "" { + attrs.PutEmptySlice(containerImageRepoDigests).AppendEmpty().SetStr(containerStatus.ImageRepoDigest) + } } } } diff --git a/processor/k8sattributesprocessor/processor_test.go b/processor/k8sattributesprocessor/processor_test.go index 5236d0a8b8d6..c385dfcd841c 100644 --- a/processor/k8sattributesprocessor/processor_test.go +++ b/processor/k8sattributesprocessor/processor_test.go @@ -950,7 +950,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { name string op func(kp *kubernetesprocessor) resourceGens []generateResourceFunc - wantAttrs map[string]string + wantAttrs map[string]any }{ { name: "all-by-name", @@ -982,7 +982,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { withPodUID("19f651bc-73e4-410f-b3e9-f0241679d3b8"), withContainerName("app"), }, - wantAttrs: map[string]string{ + wantAttrs: map[string]any{ conventions.AttributeK8SPodUID: "19f651bc-73e4-410f-b3e9-f0241679d3b8", conventions.AttributeK8SContainerName: "app", conventions.AttributeContainerImageName: "test/app", @@ -1019,7 +1019,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { withPodUID("19f651bc-73e4-410f-b3e9-f0241679d3b8"), withContainerID("767dc30d4fece77038e8ec2585a33471944d0b754659af7aa7e101181418f0dd"), }, - wantAttrs: map[string]string{ + wantAttrs: map[string]any{ conventions.AttributeK8SPodUID: "19f651bc-73e4-410f-b3e9-f0241679d3b8", conventions.AttributeContainerID: "767dc30d4fece77038e8ec2585a33471944d0b754659af7aa7e101181418f0dd", conventions.AttributeK8SContainerName: "app", @@ -1056,7 +1056,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { withPodUID("19f651bc-73e4-410f-b3e9-f0241679d3b8"), withContainerName("app"), }, - wantAttrs: map[string]string{ + wantAttrs: map[string]any{ conventions.AttributeK8SPodUID: "19f651bc-73e4-410f-b3e9-f0241679d3b8", conventions.AttributeK8SContainerName: "app", conventions.AttributeContainerImageName: "test/app", @@ -1064,7 +1064,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { }, }, { - name: "container-id-only", + name: "container-id-with-runid", op: func(kp *kubernetesprocessor) { kp.kc.(*fakeClient).Pods[newPodIdentifier("connection", "k8s.pod.ip", "1.1.1.1")] = &kube.Pod{ Containers: kube.PodContainers{ @@ -1073,6 +1073,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { Statuses: map[int]kube.ContainerStatus{ 0: {ContainerID: "fcd58c97330c1dc6615bd520031f6a703a7317cd92adc96013c4dd57daad0b5f"}, 1: {ContainerID: "6a7f1a598b5dafec9c193f8f8d63f6e5839b8b0acd2fe780f94285e26c05580e"}, + 2: {ContainerID: "5ba4e0e5a5eb1f37bc6e7fc76495914400a3ee309d8828d16407e4b3d5410848"}, }, }, }, @@ -1084,7 +1085,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { withContainerName("app"), withContainerRunID("1"), }, - wantAttrs: map[string]string{ + wantAttrs: map[string]any{ kube.K8sIPLabelName: "1.1.1.1", conventions.AttributeK8SContainerName: "app", conventions.AttributeK8SContainerRestartCount: "1", @@ -1112,12 +1113,37 @@ func TestProcessorAddContainerAttributes(t *testing.T) { withPassthroughIP("1.1.1.1"), withContainerName("app"), }, - wantAttrs: map[string]string{ + wantAttrs: map[string]any{ kube.K8sIPLabelName: "1.1.1.1", conventions.AttributeK8SContainerName: "app", conventions.AttributeContainerID: "5ba4e0e5a5eb1f37bc6e7fc76495914400a3ee309d8828d16407e4b3d5410848", }, }, + { + name: "container-repo-digests", + op: func(kp *kubernetesprocessor) { + kp.kc.(*fakeClient).Pods[newPodIdentifier("connection", "k8s.pod.ip", "1.1.1.1")] = &kube.Pod{ + Containers: kube.PodContainers{ + ByName: map[string]*kube.Container{ + "app": { + Statuses: map[int]kube.ContainerStatus{ + 2: {ImageRepoDigest: "docker.io/otel/collector@sha256:deadbeef02"}, + }, + }, + }, + }, + } + }, + resourceGens: []generateResourceFunc{ + withPassthroughIP("1.1.1.1"), + withContainerName("app"), + }, + wantAttrs: map[string]any{ + kube.K8sIPLabelName: "1.1.1.1", + conventions.AttributeK8SContainerName: "app", + containerImageRepoDigests: []string{"docker.io/otel/collector@sha256:deadbeef02"}, + }, + }, { name: "container-name-mismatch", op: func(kp *kubernetesprocessor) { @@ -1140,7 +1166,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { withContainerName("new-app"), withContainerRunID("0"), }, - wantAttrs: map[string]string{ + wantAttrs: map[string]any{ kube.K8sIPLabelName: "1.1.1.1", conventions.AttributeK8SContainerName: "new-app", conventions.AttributeK8SContainerRestartCount: "0", @@ -1167,7 +1193,7 @@ func TestProcessorAddContainerAttributes(t *testing.T) { withContainerName("app"), withContainerRunID("1"), }, - wantAttrs: map[string]string{ + wantAttrs: map[string]any{ kube.K8sIPLabelName: "1.1.1.1", conventions.AttributeK8SContainerName: "app", conventions.AttributeK8SContainerRestartCount: "1", @@ -1194,7 +1220,12 @@ func TestProcessorAddContainerAttributes(t *testing.T) { m.assertResource(0, func(r pcommon.Resource) { require.Equal(t, len(tt.wantAttrs), r.Attributes().Len()) for k, v := range tt.wantAttrs { - assertResourceHasStringAttribute(t, r, k, v) + switch val := v.(type) { + case string: + assertResourceHasStringAttribute(t, r, k, val) + case []string: + assertResourceHasStringSlice(t, r, k, val) + } } }) } @@ -1476,6 +1507,17 @@ func assertResourceHasStringAttribute(t *testing.T, r pcommon.Resource, k, v str assert.EqualValues(t, v, got.Str(), "attribute %s is not equal to %s", k, v) } +func assertResourceHasStringSlice(t *testing.T, r pcommon.Resource, k string, v []string) { + got, ok := r.Attributes().Get(k) + require.True(t, ok, fmt.Sprintf("resource does not contain attribute %s", k)) + assert.EqualValues(t, pcommon.ValueTypeSlice, got.Type(), "attribute %s is not of type slice", k) + slice := got.Slice() + for i := 0; i < slice.Len(); i++ { + assert.EqualValues(t, pcommon.ValueTypeStr, slice.At(i).Type()) + assert.EqualValues(t, v[i], slice.At(i).AsString(), "attribute %s[%d] is not equal to %s", k, i, v[i]) + } +} + func Test_intFromAttribute(t *testing.T) { tests := []struct { name string diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/configmap.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/configmap.yaml index 72cbd3a03e56..2a55e8a87bf0 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/configmap.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/configmap.yaml @@ -48,6 +48,7 @@ data: - k8s.cluster.uid - container.id - container.image.name + - container.image.repo_digests - container.image.tag pod_association: - sources: diff --git a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/configmap.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/configmap.yaml index 39a1931d4777..81cb2e79ecbe 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/configmap.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/configmap.yaml @@ -43,6 +43,7 @@ data: - container.id - container.image.name - container.image.tag + - container.image.repo_digests - k8s.cluster.uid pod_association: - sources: diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/configmap.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/configmap.yaml index e74d54a1d952..6521187953f4 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/configmap.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/configmap.yaml @@ -47,6 +47,7 @@ data: - k8s.node.name - container.id - container.image.name + - container.image.repo_digests - container.image.tag pod_association: - sources: