diff --git a/README.md b/README.md index b8a18c94e1..6a1cdcd86c 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,8 @@ spec: EOF ``` +When using sidecar mode the OpenTelemetry collector container will have the environment variable `OTEL_RESOURCE_ATTRIBUTES`set with Kubernetes resource attributes, ready to be consumed by the [resourcedetection](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/resourcedetectionprocessor) processor. + ### OpenTelemetry auto-instrumentation injection The operator can inject and configure OpenTelemetry auto-instrumentation libraries. Currently Java, NodeJS and Python are supported. diff --git a/pkg/constants/env.go b/pkg/constants/env.go new file mode 100644 index 0000000000..0c4070905c --- /dev/null +++ b/pkg/constants/env.go @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package constants + +const ( + EnvOTELServiceName = "OTEL_SERVICE_NAME" + EnvOTELExporterOTLPEndpoint = "OTEL_EXPORTER_OTLP_ENDPOINT" + EnvOTELResourceAttrs = "OTEL_RESOURCE_ATTRIBUTES" + EnvOTELPropagators = "OTEL_PROPAGATORS" + EnvOTELTracesSampler = "OTEL_TRACES_SAMPLER" + EnvOTELTracesSamplerArg = "OTEL_TRACES_SAMPLER_ARG" + + EnvPodName = "OTEL_RESOURCE_ATTRIBUTES_POD_NAME" + EnvPodUID = "OTEL_RESOURCE_ATTRIBUTES_POD_UID" + EnvNodeName = "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME" +) diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index 161e08ad29..a5bf12e656 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -31,22 +31,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" ) const ( volumeName = "opentelemetry-auto-instrumentation" initContainerName = "opentelemetry-auto-instrumentation" - - envOTELServiceName = "OTEL_SERVICE_NAME" - envOTELExporterOTLPEndpoint = "OTEL_EXPORTER_OTLP_ENDPOINT" - envOTELResourceAttrs = "OTEL_RESOURCE_ATTRIBUTES" - envOTELPropagators = "OTEL_PROPAGATORS" - envOTELTracesSampler = "OTEL_TRACES_SAMPLER" - envOTELTracesSamplerArg = "OTEL_TRACES_SAMPLER_ARG" - - envPodName = "OTEL_RESOURCE_ATTRIBUTES_POD_NAME" - envPodUID = "OTEL_RESOURCE_ATTRIBUTES_POD_UID" - envNodeName = "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME" ) // inject a new sidecar container to the given pod, based on the given OpenTelemetryCollector. @@ -101,18 +91,18 @@ func (i *sdkInjector) injectCommonEnvVar(otelinst v1alpha1.Instrumentation, pod func (i *sdkInjector) injectCommonSDKConfig(ctx context.Context, otelinst v1alpha1.Instrumentation, ns corev1.Namespace, pod corev1.Pod) corev1.Pod { container := &pod.Spec.Containers[0] resourceMap := i.createResourceMap(ctx, otelinst, ns, pod) - idx := getIndexOfEnv(container.Env, envOTELServiceName) + idx := getIndexOfEnv(container.Env, constants.EnvOTELServiceName) if idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ - Name: envOTELServiceName, + Name: constants.EnvOTELServiceName, Value: chooseServiceName(pod, resourceMap), }) } if otelinst.Spec.Exporter.Endpoint != "" { - idx = getIndexOfEnv(container.Env, envOTELExporterOTLPEndpoint) + idx = getIndexOfEnv(container.Env, constants.EnvOTELExporterOTLPEndpoint) if idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ - Name: envOTELExporterOTLPEndpoint, + Name: constants.EnvOTELExporterOTLPEndpoint, Value: otelinst.Spec.Endpoint, }) } @@ -121,45 +111,45 @@ func (i *sdkInjector) injectCommonSDKConfig(ctx context.Context, otelinst v1alph // Some attributes might be empty, we should get them via k8s downward API if resourceMap[string(semconv.K8SPodNameKey)] == "" { container.Env = append(container.Env, corev1.EnvVar{ - Name: envPodName, + Name: constants.EnvPodName, ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "metadata.name", }, }, }) - resourceMap[string(semconv.K8SPodNameKey)] = fmt.Sprintf("$(%s)", envPodName) + resourceMap[string(semconv.K8SPodNameKey)] = fmt.Sprintf("$(%s)", constants.EnvPodName) } if otelinst.Spec.Resource.AddK8sUIDAttributes { if resourceMap[string(semconv.K8SPodUIDKey)] == "" { container.Env = append(container.Env, corev1.EnvVar{ - Name: envPodUID, + Name: constants.EnvPodUID, ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "metadata.uid", }, }, }) - resourceMap[string(semconv.K8SPodUIDKey)] = fmt.Sprintf("$(%s)", envPodUID) + resourceMap[string(semconv.K8SPodUIDKey)] = fmt.Sprintf("$(%s)", constants.EnvPodUID) } } if resourceMap[string(semconv.K8SNodeNameKey)] == "" { container.Env = append(container.Env, corev1.EnvVar{ - Name: envNodeName, + Name: constants.EnvNodeName, ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "spec.nodeName", }, }, }) - resourceMap[string(semconv.K8SNodeNameKey)] = fmt.Sprintf("$(%s)", envNodeName) + resourceMap[string(semconv.K8SNodeNameKey)] = fmt.Sprintf("$(%s)", constants.EnvNodeName) } - idx = getIndexOfEnv(container.Env, envOTELResourceAttrs) + idx = getIndexOfEnv(container.Env, constants.EnvOTELResourceAttrs) resStr := resourceMapToStr(resourceMap) if idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ - Name: envOTELResourceAttrs, + Name: constants.EnvOTELResourceAttrs, Value: resStr, }) } else { @@ -169,27 +159,27 @@ func (i *sdkInjector) injectCommonSDKConfig(ctx context.Context, otelinst v1alph container.Env[idx].Value += resStr } - idx = getIndexOfEnv(container.Env, envOTELPropagators) + idx = getIndexOfEnv(container.Env, constants.EnvOTELPropagators) if idx == -1 && len(otelinst.Spec.Propagators) > 0 { propagators := *(*[]string)((unsafe.Pointer(&otelinst.Spec.Propagators))) container.Env = append(container.Env, corev1.EnvVar{ - Name: envOTELPropagators, + Name: constants.EnvOTELPropagators, Value: strings.Join(propagators, ","), }) } - idx = getIndexOfEnv(container.Env, envOTELTracesSampler) + idx = getIndexOfEnv(container.Env, constants.EnvOTELTracesSampler) // configure sampler only if it is configured in the CR if idx == -1 && otelinst.Spec.Sampler.Type != "" { - idxSamplerArg := getIndexOfEnv(container.Env, envOTELTracesSamplerArg) + idxSamplerArg := getIndexOfEnv(container.Env, constants.EnvOTELTracesSamplerArg) if idxSamplerArg == -1 { container.Env = append(container.Env, corev1.EnvVar{ - Name: envOTELTracesSampler, + Name: constants.EnvOTELTracesSampler, Value: string(otelinst.Spec.Sampler.Type), }) if otelinst.Spec.Sampler.Argument != "" { container.Env = append(container.Env, corev1.EnvVar{ - Name: envOTELTracesSamplerArg, + Name: constants.EnvOTELTracesSamplerArg, Value: otelinst.Spec.Sampler.Argument, }) } @@ -223,7 +213,7 @@ func chooseServiceName(pod corev1.Pod, resources map[string]string) string { func (i *sdkInjector) createResourceMap(ctx context.Context, otelinst v1alpha1.Instrumentation, ns corev1.Namespace, pod corev1.Pod) map[string]string { // get existing resources env var and parse it into a map existingRes := map[string]bool{} - existingResourceEnvIdx := getIndexOfEnv(pod.Spec.Containers[0].Env, envOTELResourceAttrs) + existingResourceEnvIdx := getIndexOfEnv(pod.Spec.Containers[0].Env, constants.EnvOTELResourceAttrs) if existingResourceEnvIdx > -1 { existingResArr := strings.Split(pod.Spec.Containers[0].Env[existingResourceEnvIdx].Value, ",") for _, kv := range existingResArr { diff --git a/pkg/sidecar/attributes.go b/pkg/sidecar/attributes.go new file mode 100644 index 0000000000..e40492f786 --- /dev/null +++ b/pkg/sidecar/attributes.go @@ -0,0 +1,120 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sidecar contains operations related to sidecar manipulation (Add, update, remove). +package sidecar + +import ( + "fmt" + "sort" + "strings" + + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" +) + +const resourceAttributesEnvName = "OTEL_RESOURCE_ATTRIBUTES" + +type podReferences struct { + replicaset *appsv1.ReplicaSet + deployment *appsv1.Deployment +} + +// getResourceAttributesEnv returns a list of environment variables. The list contains OTEL_RESOURCE_ATTRIBUTES and additional environment variables that use Kubernetes downward API to read pod specification. +// see: https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/ +func getResourceAttributesEnv(ns corev1.Namespace, podReferences podReferences) []corev1.EnvVar { + + var envvars []corev1.EnvVar + + attributes := map[attribute.Key]string{ + semconv.K8SPodNameKey: fmt.Sprintf("$(%s)", constants.EnvPodName), + semconv.K8SPodUIDKey: fmt.Sprintf("$(%s)", constants.EnvPodUID), + semconv.K8SNodeNameKey: fmt.Sprintf("$(%s)", constants.EnvNodeName), + semconv.K8SNamespaceNameKey: ns.Name, + } + + if podReferences.deployment != nil { + attributes[semconv.K8SDeploymentUIDKey] = string(podReferences.deployment.UID) + attributes[semconv.K8SDeploymentNameKey] = string(podReferences.deployment.Name) + } + + if podReferences.replicaset != nil { + attributes[semconv.K8SReplicaSetUIDKey] = string(podReferences.replicaset.UID) + attributes[semconv.K8SReplicaSetNameKey] = string(podReferences.replicaset.Name) + } + + envvars = append(envvars, corev1.EnvVar{ + Name: constants.EnvPodName, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }) + + envvars = append(envvars, corev1.EnvVar{ + Name: constants.EnvPodUID, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.uid", + }, + }, + }) + + envvars = append(envvars, corev1.EnvVar{ + Name: constants.EnvNodeName, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }) + + envvars = append(envvars, corev1.EnvVar{ + Name: resourceAttributesEnvName, + Value: mapToValue(attributes), + }) + + return envvars +} + +func mapToValue(attributesMap map[attribute.Key]string) string { + var parts []string + + // Sort it to make it predictable + keys := make([]string, 0, len(attributesMap)) + for k := range attributesMap { + keys = append(keys, string(k)) + } + sort.Strings(keys) + + for _, key := range keys { + parts = append(parts, fmt.Sprintf("%s=%s", key, attributesMap[attribute.Key(key)])) + } + return strings.Join(parts, ",") +} + +// check if container doesn't have already the OTEL_RESOURCE_ATTRIBUTES, we don't want to override it if it's already specified. +func hasResourceAttributeEnvVar(envvars []corev1.EnvVar) bool { + for _, env := range envvars { + if env.Name == resourceAttributesEnvName { + return true + } + } + return false +} diff --git a/pkg/sidecar/attributes_test.go b/pkg/sidecar/attributes_test.go new file mode 100644 index 0000000000..b8dc62f58b --- /dev/null +++ b/pkg/sidecar/attributes_test.go @@ -0,0 +1,189 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sidecar + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + appv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" +) + +func TestGetAttributesEnvNoPodReferences(t *testing.T) { + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-ns", + }, + } + references := podReferences{} + envs := getResourceAttributesEnv(ns, references) + + expectedEnv := []corev1.EnvVar{ + { + Name: constants.EnvPodName, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: constants.EnvPodUID, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.uid", + }, + }, + }, + { + Name: constants.EnvNodeName, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: resourceAttributesEnvName, + Value: fmt.Sprintf("%s=my-ns,%s=$(%s),%s=$(%s),%s=$(%s)", + semconv.K8SNamespaceNameKey, + semconv.K8SNodeNameKey, + constants.EnvNodeName, + semconv.K8SPodNameKey, + constants.EnvPodName, + semconv.K8SPodUIDKey, + constants.EnvPodUID, + ), + }, + } + + assert.Equal(t, expectedEnv, envs) +} + +func TestGetAttributesEnvWithPodReferences(t *testing.T) { + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-ns", + }, + } + references := podReferences{ + deployment: &appv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-deployment", + UID: "uuid-dep", + }, + }, + replicaset: &appv1.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-replicaset", + UID: "uuid-replicaset", + }, + }, + } + envs := getResourceAttributesEnv(ns, references) + + expectedEnv := []corev1.EnvVar{ + { + Name: constants.EnvPodName, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: constants.EnvPodUID, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.uid", + }, + }, + }, + { + Name: constants.EnvNodeName, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: resourceAttributesEnvName, + Value: fmt.Sprintf("%s=my-deployment,%s=uuid-dep,%s=my-ns,%s=$(%s),%s=$(%s),%s=$(%s),%s=my-replicaset,%s=uuid-replicaset", + semconv.K8SDeploymentNameKey, + semconv.K8SDeploymentUIDKey, + semconv.K8SNamespaceNameKey, + semconv.K8SNodeNameKey, + constants.EnvNodeName, + semconv.K8SPodNameKey, + constants.EnvPodName, + semconv.K8SPodUIDKey, + constants.EnvPodUID, + semconv.K8SReplicaSetNameKey, + semconv.K8SReplicaSetUIDKey, + ), + }, + } + + assert.Equal(t, expectedEnv, envs) +} + +func TestHasResourceAttributeEnvVar(t *testing.T) { + for _, tt := range []struct { + desc string + expected bool + env []corev1.EnvVar + }{ + { + "has-attributes", true, []corev1.EnvVar{ + { + Name: resourceAttributesEnvName, + Value: fmt.Sprintf("%s=my-deployment,%s=uuid-dep,%s=my-ns,%s=$(%s),%s=$(%s),%s=$(%s),%s=my-replicaset,%s=uuid-replicaset", + semconv.K8SDeploymentNameKey, + semconv.K8SDeploymentUIDKey, + semconv.K8SNamespaceNameKey, + semconv.K8SNodeNameKey, + constants.EnvNodeName, + semconv.K8SPodNameKey, + constants.EnvPodName, + semconv.K8SPodUIDKey, + constants.EnvPodUID, + semconv.K8SReplicaSetNameKey, + semconv.K8SReplicaSetUIDKey, + ), + }, + }, + }, + + { + "does-not-have-attributes", false, []corev1.EnvVar{ + { + Name: "other_env", + Value: "other_value", + }, + }, + }, + } { + t.Run(tt.desc, func(t *testing.T) { + assert.Equal(t, tt.expected, hasResourceAttributeEnvVar(tt.env)) + }) + } +} diff --git a/pkg/sidecar/pod.go b/pkg/sidecar/pod.go index 475b5828a8..593d3d0c3a 100644 --- a/pkg/sidecar/pod.go +++ b/pkg/sidecar/pod.go @@ -32,10 +32,13 @@ const ( ) // add a new sidecar container to the given pod, based on the given OpenTelemetryCollector. -func add(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector, pod corev1.Pod) (corev1.Pod, error) { +func add(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector, pod corev1.Pod, attributes []corev1.EnvVar) (corev1.Pod, error) { // add the container volumes := collector.Volumes(cfg, otelcol) container := collector.Container(cfg, logger, otelcol) + if !hasResourceAttributeEnvVar(container.Env) { + container.Env = append(container.Env, attributes...) + } pod.Spec.Containers = append(pod.Spec.Containers, container) pod.Spec.Volumes = append(pod.Spec.Volumes, volumes...) diff --git a/pkg/sidecar/pod_test.go b/pkg/sidecar/pod_test.go index a0e36e9799..43dec668cd 100644 --- a/pkg/sidecar/pod_test.go +++ b/pkg/sidecar/pod_test.go @@ -49,7 +49,7 @@ func TestAddSidecarWhenNoSidecarExists(t *testing.T) { cfg := config.New(config.WithCollectorImage("some-default-image")) // test - changed, err := add(cfg, logger, otelcol, pod) + changed, err := add(cfg, logger, otelcol, pod, nil) // verify assert.NoError(t, err) @@ -74,7 +74,7 @@ func TestAddSidecarWhenOneExistsAlready(t *testing.T) { cfg := config.New(config.WithCollectorImage("some-default-image")) // test - changed, err := add(cfg, logger, otelcol, pod) + changed, err := add(cfg, logger, otelcol, pod, nil) // verify assert.NoError(t, err) @@ -145,3 +145,37 @@ func TestExistsIn(t *testing.T) { }) } } + +func TestAddSidecarWithAditionalEnv(t *testing.T) { + // prepare + pod := corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "my-app"}, + }, + }, + } + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "otelcol-sample", + Namespace: "some-app", + }, + } + cfg := config.New(config.WithCollectorImage("some-default-image")) + + extraEnv := corev1.EnvVar{ + Name: "extraenv", + Value: "extravalue", + } + + // test + changed, err := add(cfg, logger, otelcol, pod, []corev1.EnvVar{ + extraEnv, + }) + + // verify + assert.NoError(t, err) + assert.Len(t, changed.Spec.Containers, 2) + assert.Contains(t, changed.Spec.Containers[1].Env, extraEnv) + +} diff --git a/pkg/sidecar/podmutator.go b/pkg/sidecar/podmutator.go index e951dd6d14..f7d0f6a713 100644 --- a/pkg/sidecar/podmutator.go +++ b/pkg/sidecar/podmutator.go @@ -20,7 +20,9 @@ import ( "strings" "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -68,7 +70,6 @@ func (p *sidecarPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod } // from this point and on, a sidecar is wanted - // check whether there's a sidecar already -- return the same pod if that's the case. if existsIn(pod) { logger.V(1).Info("pod already has sidecar in it, skipping injection") @@ -88,10 +89,15 @@ func (p *sidecarPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod return pod, err } + // getting pod references, if any + references := p.podReferences(ctx, pod.OwnerReferences, ns) + attributes := getResourceAttributesEnv(ns, references) + // once it's been determined that a sidecar is desired, none exists yet, and we know which instance it should talk to, // we should add the sidecar. logger.V(1).Info("injecting sidecar into pod", "otelcol-namespace", otelcol.Namespace, "otelcol-name", otelcol.Name) - return add(p.config, p.logger, otelcol, pod) + + return add(p.config, p.logger, otelcol, pod, attributes) } func (p *sidecarPodMutator) getCollectorInstance(ctx context.Context, ns corev1.Namespace, ann string) (v1alpha1.OpenTelemetryCollector, error) { @@ -138,3 +144,49 @@ func (p *sidecarPodMutator) selectCollectorInstance(ctx context.Context, ns core return sidecars[0], nil } } + +func (p *sidecarPodMutator) podReferences(ctx context.Context, ownerReferences []metav1.OwnerReference, ns corev1.Namespace) podReferences { + references := &podReferences{} + replicaSet := p.getReplicaSetReference(ctx, ownerReferences, ns) + if replicaSet != nil { + references.replicaset = replicaSet + deployment := p.getDeploymentReference(ctx, replicaSet) + if deployment != nil { + references.deployment = deployment + } + } + return *references +} + +func (p *sidecarPodMutator) getReplicaSetReference(ctx context.Context, ownerReferences []metav1.OwnerReference, ns corev1.Namespace) *appsv1.ReplicaSet { + replicaSetName := findOwnerReferenceKind(ownerReferences, "ReplicaSet") + if replicaSetName != "" { + replicaSet := &appsv1.ReplicaSet{} + err := p.client.Get(ctx, types.NamespacedName{Name: replicaSetName, Namespace: ns.Name}, replicaSet) + if err == nil { + return replicaSet + } + } + return nil +} + +func (p *sidecarPodMutator) getDeploymentReference(ctx context.Context, replicaSet *appsv1.ReplicaSet) *appsv1.Deployment { + deploymentName := findOwnerReferenceKind(replicaSet.OwnerReferences, "Deployment") + if deploymentName != "" { + deployment := &appsv1.Deployment{} + err := p.client.Get(ctx, types.NamespacedName{Name: deploymentName, Namespace: replicaSet.Namespace}, deployment) + if err == nil { + return deployment + } + } + return nil +} + +func findOwnerReferenceKind(references []metav1.OwnerReference, kind string) string { + for _, reference := range references { + if reference.Kind == kind { + return reference.Name + } + } + return "" +} diff --git a/tests/e2e/smoke-sidecar/01-assert.yaml b/tests/e2e/smoke-sidecar/01-assert.yaml index 40a7b819bc..358711a288 100644 --- a/tests/e2e/smoke-sidecar/01-assert.yaml +++ b/tests/e2e/smoke-sidecar/01-assert.yaml @@ -9,5 +9,11 @@ spec: containers: - name: myapp - name: otc-container + env: + - name: POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_POD_UID + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_RESOURCE_ATTRIBUTES status: phase: Running