diff --git a/.chloggen/feature_3090.yaml b/.chloggen/feature_3090.yaml new file mode 100755 index 0000000000..ad1f98094d --- /dev/null +++ b/.chloggen/feature_3090.yaml @@ -0,0 +1,16 @@ +# 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. collector, target allocator, auto-instrumentation, opamp, github action) +component: auto-instrumentation + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Not ignore the `instrumentation.opentelemetry.io/container-names` annotation when the multi-instrumentation is enabled" + +# One or more tracking issues related to the change +issues: [3090] + +# (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: diff --git a/pkg/instrumentation/helper.go b/pkg/instrumentation/helper.go index 8306f773ae..1968fe8973 100644 --- a/pkg/instrumentation/helper.go +++ b/pkg/instrumentation/helper.go @@ -16,11 +16,13 @@ package instrumentation import ( "fmt" + "regexp" "sort" "strings" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/strings/slices" "github.com/open-telemetry/opentelemetry-operator/internal/naming" @@ -77,15 +79,9 @@ func isAutoInstrumentationInjected(pod corev1.Pod) bool { // Look for duplicates in the provided containers. func findDuplicatedContainers(ctrs []string) error { - // Merge is needed because of multiple containers can be provided for single instrumentation. - mergedContainers := strings.Join(ctrs, ",") - - // Split all containers. - splitContainers := strings.Split(mergedContainers, ",") - countMap := make(map[string]int) var duplicates []string - for _, str := range splitContainers { + for _, str := range ctrs { countMap[str]++ } @@ -111,7 +107,7 @@ func findDuplicatedContainers(ctrs []string) error { // Return positive for instrumentation with defined containers. func isInstrWithContainers(inst instrumentationWithContainers) int { - if inst.Containers != "" { + if len(inst.Containers) > 0 { return 1 } @@ -120,7 +116,7 @@ func isInstrWithContainers(inst instrumentationWithContainers) int { // Return positive for instrumentation without defined containers. func isInstrWithoutContainers(inst instrumentationWithContainers) int { - if inst.Containers == "" { + if len(inst.Containers) == 0 { return 1 } @@ -133,3 +129,33 @@ func volumeSize(quantity *resource.Quantity) *resource.Quantity { } return quantity } + +func isValidContainersAnnotation(containersAnnotation string) error { + if containersAnnotation == "" { + return nil + } + + matched, err := regexp.MatchString("^[a-zA-Z0-9-,]+$", containersAnnotation) + if err != nil { + return fmt.Errorf("error while checking for instrumentation container annotations %w", err) + } + if !matched { + return fmt.Errorf("not valid characters included in the instrumentation container annotation %s", containersAnnotation) + } + return nil +} + +// setContainersFromAnnotation sets the containers associated to one intrumentation based on the content of the provided annotation. +func setContainersFromAnnotation(inst *instrumentationWithContainers, annotation string, ns metav1.ObjectMeta, pod metav1.ObjectMeta) error { + annotationValue := annotationValue(ns, pod, annotation) + if annotationValue == "" { + return nil + } + + if err := isValidContainersAnnotation(annotationValue); err != nil { + return err + } + languageContainers := strings.Split(annotationValue, ",") + inst.Containers = append(inst.Containers, languageContainers...) + return nil +} diff --git a/pkg/instrumentation/helper_test.go b/pkg/instrumentation/helper_test.go index 72db0b4f21..d852c94a4a 100644 --- a/pkg/instrumentation/helper_test.go +++ b/pkg/instrumentation/helper_test.go @@ -170,12 +170,12 @@ func TestDuplicatedContainers(t *testing.T) { }{ { name: "No duplicates", - containers: []string{"app1,app2", "app3", "app4,app5"}, + containers: []string{"app1", "app2", "app3", "app4", "app5"}, expectedDuplicates: nil, }, { name: "Duplicates in containers", - containers: []string{"app1,app2", "app1", "app1,app3,app4", "app4"}, + containers: []string{"app1", "app2", "app1", "app1", "app3", "app4", "app4"}, expectedDuplicates: fmt.Errorf("duplicated container names detected: [app1 app4]"), }, } @@ -196,12 +196,12 @@ func TestInstrWithContainers(t *testing.T) { }{ { name: "No containers", - containers: instrumentationWithContainers{Containers: ""}, + containers: instrumentationWithContainers{Containers: []string{}}, expectedResult: 0, }, { name: "With containers", - containers: instrumentationWithContainers{Containers: "ct1"}, + containers: instrumentationWithContainers{Containers: []string{"ct1"}}, expectedResult: 1, }, } @@ -222,12 +222,12 @@ func TestInstrWithoutContainers(t *testing.T) { }{ { name: "No containers", - containers: instrumentationWithContainers{Containers: ""}, + containers: instrumentationWithContainers{Containers: []string{}}, expectedResult: 1, }, { name: "With containers", - containers: instrumentationWithContainers{Containers: "ct1"}, + containers: instrumentationWithContainers{Containers: []string{"ct1"}}, expectedResult: 0, }, } diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index 7b75576e3d..f0f1f08d52 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -22,6 +22,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" @@ -46,7 +47,7 @@ type instPodMutator struct { type instrumentationWithContainers struct { Instrumentation *v1alpha1.Instrumentation - Containers string + Containers []string AdditionalAnnotations map[string]string } @@ -61,84 +62,77 @@ type languageInstrumentations struct { Sdk instrumentationWithContainers } -// Check if single instrumentation is configured for Pod and return which is configured. -func (langInsts languageInstrumentations) isSingleInstrumentationEnabled() bool { - count := 0 - - if langInsts.Java.Instrumentation != nil { - count++ - } - if langInsts.NodeJS.Instrumentation != nil { - count++ - } - if langInsts.Python.Instrumentation != nil { - count++ - } - if langInsts.DotNet.Instrumentation != nil { - count++ - } - if langInsts.ApacheHttpd.Instrumentation != nil { - count++ - } - if langInsts.Nginx.Instrumentation != nil { - count++ - } - if langInsts.Go.Instrumentation != nil { - count++ - } - if langInsts.Sdk.Instrumentation != nil { - count++ - } - - return count == 1 -} - // Check if specific containers are provided for configured instrumentation. -func (langInsts languageInstrumentations) areContainerNamesConfiguredForMultipleInstrumentations() (bool, error) { +func (langInsts languageInstrumentations) areInstrumentedContainersCorrect() (bool, error) { var instrWithoutContainers int var instrWithContainers int var allContainers []string + var instrumentationWithNoContainers bool // Check for instrumentations with and without containers. if langInsts.Java.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.Java) instrWithoutContainers += isInstrWithoutContainers(langInsts.Java) - allContainers = append(allContainers, langInsts.Java.Containers) + allContainers = append(allContainers, langInsts.Java.Containers...) + if len(langInsts.Java.Containers) == 0 { + instrumentationWithNoContainers = true + } } if langInsts.NodeJS.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.NodeJS) instrWithoutContainers += isInstrWithoutContainers(langInsts.NodeJS) - allContainers = append(allContainers, langInsts.NodeJS.Containers) + allContainers = append(allContainers, langInsts.NodeJS.Containers...) + if len(langInsts.NodeJS.Containers) == 0 { + instrumentationWithNoContainers = true + } } if langInsts.Python.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.Python) instrWithoutContainers += isInstrWithoutContainers(langInsts.Python) - allContainers = append(allContainers, langInsts.Python.Containers) + allContainers = append(allContainers, langInsts.Python.Containers...) + if len(langInsts.Python.Containers) == 0 { + instrumentationWithNoContainers = true + } } if langInsts.DotNet.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.DotNet) instrWithoutContainers += isInstrWithoutContainers(langInsts.DotNet) - allContainers = append(allContainers, langInsts.DotNet.Containers) + allContainers = append(allContainers, langInsts.DotNet.Containers...) + if len(langInsts.DotNet.Containers) == 0 { + instrumentationWithNoContainers = true + } } if langInsts.ApacheHttpd.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.ApacheHttpd) instrWithoutContainers += isInstrWithoutContainers(langInsts.ApacheHttpd) - allContainers = append(allContainers, langInsts.ApacheHttpd.Containers) + allContainers = append(allContainers, langInsts.ApacheHttpd.Containers...) + if len(langInsts.ApacheHttpd.Containers) == 0 { + instrumentationWithNoContainers = true + } } if langInsts.Nginx.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.Nginx) instrWithoutContainers += isInstrWithoutContainers(langInsts.Nginx) - allContainers = append(allContainers, langInsts.Nginx.Containers) + allContainers = append(allContainers, langInsts.Nginx.Containers...) + if len(langInsts.Nginx.Containers) == 0 { + instrumentationWithNoContainers = true + } } if langInsts.Go.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.Go) instrWithoutContainers += isInstrWithoutContainers(langInsts.Go) - allContainers = append(allContainers, langInsts.Go.Containers) + allContainers = append(allContainers, langInsts.Go.Containers...) + if len(langInsts.Go.Containers) == 0 { + instrumentationWithNoContainers = true + } } if langInsts.Sdk.Instrumentation != nil { instrWithContainers += isInstrWithContainers(langInsts.Sdk) instrWithoutContainers += isInstrWithoutContainers(langInsts.Sdk) - allContainers = append(allContainers, langInsts.Sdk.Containers) + allContainers = append(allContainers, langInsts.Sdk.Containers...) + if len(langInsts.Sdk.Containers) == 0 { + instrumentationWithNoContainers = true + } } // Look for duplicated containers. @@ -161,11 +155,29 @@ func (langInsts languageInstrumentations) areContainerNamesConfiguredForMultiple return false, fmt.Errorf("instrumentation configuration not provided") } + enabledInstrumentations := instrWithContainers + instrWithoutContainers + + if enabledInstrumentations > 1 && instrumentationWithNoContainers { + return false, fmt.Errorf("incorrect instrumentation configuration - please provide container names for all instrumentations") + } + return true, nil } // Set containers for configured instrumentation. -func (langInsts *languageInstrumentations) setInstrumentationLanguageContainers(containers string) { +func (langInsts *languageInstrumentations) setCommonInstrumentedContainers(ns corev1.Namespace, pod corev1.Pod) error { + containersAnnotation := annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectContainerName) + if err := isValidContainersAnnotation(containersAnnotation); err != nil { + return err + } + + var containers []string + if containersAnnotation == "" { + return nil + } else { + containers = strings.Split(containersAnnotation, ",") + } + if langInsts.Java.Instrumentation != nil { langInsts.Java.Containers = containers } @@ -190,6 +202,55 @@ func (langInsts *languageInstrumentations) setInstrumentationLanguageContainers( if langInsts.Sdk.Instrumentation != nil { langInsts.Sdk.Containers = containers } + return nil +} + +func (langInsts *languageInstrumentations) setLanguageSpecificContainers(ns metav1.ObjectMeta, pod metav1.ObjectMeta) error { + inst := []struct { + iwc *instrumentationWithContainers + annotation string + }{ + { + iwc: &langInsts.Java, + annotation: annotationInjectJavaContainersName, + }, + { + iwc: &langInsts.NodeJS, + annotation: annotationInjectNodeJSContainersName, + }, + { + iwc: &langInsts.Python, + annotation: annotationInjectPythonContainersName, + }, + { + iwc: &langInsts.DotNet, + annotation: annotationInjectDotnetContainersName, + }, + { + iwc: &langInsts.Go, + annotation: annotationInjectGoContainersName, + }, + { + iwc: &langInsts.ApacheHttpd, + annotation: annotationInjectApacheHttpd, + }, + { + iwc: &langInsts.Nginx, + annotation: annotationInjectNginx, + }, + { + iwc: &langInsts.Sdk, + annotation: annotationInjectSdk, + }, + } + + for _, i := range inst { + i := i + if err := setContainersFromAnnotation(i.iwc, i.annotation, ns, pod); err != nil { + return err + } + } + return nil } var _ podmutation.PodMutator = (*instPodMutator)(nil) @@ -329,36 +390,24 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c return pod, nil } + err = insts.setCommonInstrumentedContainers(ns, pod) + if err != nil { + return pod, err + } + // We retrieve the annotation for podname if pm.config.EnableMultiInstrumentation() { - // We use annotations specific for instrumentation language - insts.Java.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectJavaContainersName) - insts.NodeJS.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectNodeJSContainersName) - insts.Python.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectPythonContainersName) - insts.DotNet.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectDotnetContainersName) - insts.Go.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectGoContainersName) - insts.ApacheHttpd.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectApacheHttpdContainersName) - insts.Nginx.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectNginxContainersName) - insts.Sdk.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectSdkContainersName) + err = insts.setLanguageSpecificContainers(ns.ObjectMeta, pod.ObjectMeta) + if err != nil { + return pod, err + } // We check if provided annotations and instrumentations are valid - ok, msg := insts.areContainerNamesConfiguredForMultipleInstrumentations() + ok, msg := insts.areInstrumentedContainersCorrect() if !ok { logger.V(1).Error(msg, "skipping instrumentation injection") return pod, nil } - } else { - // We use general annotation for container names - // only when multi instrumentation is disabled - singleInstrEnabled := insts.isSingleInstrumentationEnabled() - if singleInstrEnabled { - generalContainerNames := annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectContainerName) - insts.setInstrumentationLanguageContainers(generalContainerNames) - } else { - logger.V(1).Error(fmt.Errorf("multiple injection annotations present"), "skipping instrumentation injection") - return pod, nil - } - } // once it's been determined that instrumentation is desired, none exists yet, and we know which instance it should talk to, diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index b1bdb81c95..2eddd045f3 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -4185,789 +4185,6 @@ func TestMutatePod(t *testing.T) { config.WithEnableNodeJSInstrumentation(true), ), }, - { - name: "multi instrumentation for multiple containers feature gate enabled, container-names not used", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "multi-instrumentation-multi-containers-cn", - }, - }, - inst: v1alpha1.Instrumentation{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-inst", - Namespace: "multi-instrumentation-multi-containers-cn", - }, - Spec: v1alpha1.InstrumentationSpec{ - DotNet: v1alpha1.DotNet{ - Image: "otel/dotnet:1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - }, - }, - Java: v1alpha1.Java{ - Image: "otel/java:1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - }, - }, - NodeJS: v1alpha1.NodeJS{ - Image: "otel/nodejs:1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - }, - }, - Python: v1alpha1.Python{ - Image: "otel/python:1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - }, - }, - Exporter: v1alpha1.Exporter{ - Endpoint: "http://collector:12345", - }, - }, - }, - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - annotationInjectDotNet: "true", - annotationInjectJava: "true", - annotationInjectNodeJS: "true", - annotationInjectPython: "true", - annotationInjectDotnetContainersName: "dotnet1,dotnet2", - annotationInjectJavaContainersName: "java1,java2", - annotationInjectNodeJSContainersName: "nodejs1,nodejs2", - annotationInjectPythonContainersName: "python1,python2", - annotationInjectContainerName: "should-not-be-instrumented1,should-not-be-instrumented2", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "dotnet1", - }, - { - Name: "dotnet2", - }, - { - Name: "java1", - }, - { - Name: "java2", - }, - { - Name: "nodejs1", - }, - { - Name: "nodejs2", - }, - { - Name: "python1", - }, - { - Name: "python2", - }, - { - Name: "should-not-be-instrumented1", - }, - { - Name: "should-not-be-instrumented2", - }, - }, - }, - }, - expected: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - annotationInjectDotNet: "true", - annotationInjectJava: "true", - annotationInjectNodeJS: "true", - annotationInjectPython: "true", - annotationInjectDotnetContainersName: "dotnet1,dotnet2", - annotationInjectJavaContainersName: "java1,java2", - annotationInjectNodeJSContainersName: "nodejs1,nodejs2", - annotationInjectPythonContainersName: "python1,python2", - annotationInjectContainerName: "should-not-be-instrumented1,should-not-be-instrumented2", - }, - }, - Spec: corev1.PodSpec{ - Volumes: []corev1.Volume{ - { - Name: javaVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - SizeLimit: &defaultVolumeLimitSize, - }, - }, - }, - { - Name: nodejsVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - SizeLimit: &defaultVolumeLimitSize, - }, - }, - }, - { - Name: pythonVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - SizeLimit: &defaultVolumeLimitSize, - }, - }, - }, - { - Name: dotnetVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - SizeLimit: &defaultVolumeLimitSize, - }, - }, - }, - }, - InitContainers: []corev1.Container{ - { - Name: javaInitContainerName, - Image: "otel/java:1", - Command: []string{"cp", "/javaagent.jar", javaInstrMountPath + "/javaagent.jar"}, - VolumeMounts: []corev1.VolumeMount{{ - Name: javaVolumeName, - MountPath: javaInstrMountPath, - }}, - }, - { - Name: nodejsInitContainerName, - Image: "otel/nodejs:1", - Command: []string{"cp", "-r", "/autoinstrumentation/.", nodejsInstrMountPath}, - VolumeMounts: []corev1.VolumeMount{{ - Name: nodejsVolumeName, - MountPath: nodejsInstrMountPath, - }}, - }, - { - Name: pythonInitContainerName, - Image: "otel/python:1", - Command: []string{"cp", "-r", "/autoinstrumentation/.", pythonInstrMountPath}, - VolumeMounts: []corev1.VolumeMount{{ - Name: pythonVolumeName, - MountPath: pythonInstrMountPath, - }}, - }, - { - Name: dotnetInitContainerName, - Image: "otel/dotnet:1", - Command: []string{"cp", "-r", "/autoinstrumentation/.", dotnetInstrMountPath}, - VolumeMounts: []corev1.VolumeMount{{ - Name: dotnetVolumeName, - MountPath: dotnetInstrMountPath, - }}, - }, - }, - Containers: []corev1.Container{ - { - Name: "dotnet1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: envDotNetCoreClrEnableProfiling, - Value: dotNetCoreClrEnableProfilingEnabled, - }, - { - Name: envDotNetCoreClrProfiler, - Value: dotNetCoreClrProfilerID, - }, - { - Name: envDotNetCoreClrProfilerPath, - Value: dotNetCoreClrProfilerGlibcPath, - }, - { - Name: envDotNetStartupHook, - Value: dotNetStartupHookPath, - }, - { - Name: envDotNetAdditionalDeps, - Value: dotNetAdditionalDepsPath, - }, - { - Name: envDotNetOTelAutoHome, - Value: dotNetOTelAutoHomePath, - }, - { - Name: envDotNetSharedStore, - Value: dotNetSharedStorePath, - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "dotnet1", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=dotnet1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).dotnet1", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: dotnetVolumeName, - MountPath: dotnetInstrMountPath, - }, - }, - }, - { - Name: "dotnet2", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: envDotNetCoreClrEnableProfiling, - Value: dotNetCoreClrEnableProfilingEnabled, - }, - { - Name: envDotNetCoreClrProfiler, - Value: dotNetCoreClrProfilerID, - }, - { - Name: envDotNetCoreClrProfilerPath, - Value: dotNetCoreClrProfilerGlibcPath, - }, - { - Name: envDotNetStartupHook, - Value: dotNetStartupHookPath, - }, - { - Name: envDotNetAdditionalDeps, - Value: dotNetAdditionalDepsPath, - }, - { - Name: envDotNetOTelAutoHome, - Value: dotNetOTelAutoHomePath, - }, - { - Name: envDotNetSharedStore, - Value: dotNetSharedStorePath, - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "dotnet2", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=dotnet2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).dotnet2", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: dotnetVolumeName, - MountPath: dotnetInstrMountPath, - }, - }, - }, - { - Name: "java1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "JAVA_TOOL_OPTIONS", - Value: javaAgent, - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "java1", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=java1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).java1", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: javaVolumeName, - MountPath: javaInstrMountPath, - }, - }, - }, - { - Name: "java2", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "JAVA_TOOL_OPTIONS", - Value: javaAgent, - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "java2", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=java2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).java2", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: javaVolumeName, - MountPath: javaInstrMountPath, - }, - }, - }, - { - Name: "nodejs1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "NODE_OPTIONS", - Value: nodeRequireArgument, - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "nodejs1", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=nodejs1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).nodejs1", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: nodejsVolumeName, - MountPath: nodejsInstrMountPath, - }, - }, - }, - { - Name: "nodejs2", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "NODE_OPTIONS", - Value: nodeRequireArgument, - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "nodejs2", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=nodejs2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).nodejs2", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: nodejsVolumeName, - MountPath: nodejsInstrMountPath, - }, - }, - }, - { - Name: "python1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "PYTHONPATH", - Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), - }, - { - Name: "OTEL_EXPORTER_OTLP_PROTOCOL", - Value: "http/protobuf", - }, - { - Name: "OTEL_TRACES_EXPORTER", - Value: "otlp", - }, - { - Name: "OTEL_METRICS_EXPORTER", - Value: "otlp", - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "python1", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=python1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).python1", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: pythonVolumeName, - MountPath: pythonInstrMountPath, - }, - }, - }, - { - Name: "python2", - Env: []corev1.EnvVar{ - { - Name: "OTEL_NODE_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.hostIP", - }, - }, - }, - { - Name: "OTEL_POD_IP", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, - }, - { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "PYTHONPATH", - Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), - }, - { - Name: "OTEL_EXPORTER_OTLP_PROTOCOL", - Value: "http/protobuf", - }, - { - Name: "OTEL_TRACES_EXPORTER", - Value: "otlp", - }, - { - Name: "OTEL_METRICS_EXPORTER", - Value: "otlp", - }, - { - Name: "OTEL_SERVICE_NAME", - Value: "python2", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://collector:12345", - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.nodeName", - }, - }, - }, - { - Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=python2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=multi-instrumentation-multi-containers-cn.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).python2", - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: pythonVolumeName, - MountPath: pythonInstrMountPath, - }, - }, - }, - { - Name: "should-not-be-instrumented1", - }, - { - Name: "should-not-be-instrumented2", - }, - }, - }, - }, - config: config.New( - config.WithEnableMultiInstrumentation(true), - config.WithEnableDotNetInstrumentation(true), - config.WithEnablePythonInstrumentation(true), - config.WithEnableNodeJSInstrumentation(true), - ), - }, { name: "multi instrumentation for multiple containers feature gate disabled, multiple instrumentation annotations set", ns: corev1.Namespace{ @@ -5598,50 +4815,6 @@ func TestMutatePod(t *testing.T) { } } -func TestSingleInstrumentationEnabled(t *testing.T) { - tests := []struct { - name string - instrumentations languageInstrumentations - expectedStatus bool - expectedMsg string - }{ - { - name: "Single instrumentation enabled", - instrumentations: languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, - NodeJS: instrumentationWithContainers{Instrumentation: nil}, - }, - expectedStatus: true, - expectedMsg: "Java", - }, - { - name: "Multiple instrumentations enabled", - instrumentations: languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, - NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, - }, - expectedStatus: false, - expectedMsg: "", - }, - { - name: "Instrumentations disabled", - instrumentations: languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: nil}, - NodeJS: instrumentationWithContainers{Instrumentation: nil}, - }, - expectedStatus: false, - expectedMsg: "", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - ok := test.instrumentations.isSingleInstrumentationEnabled() - assert.Equal(t, test.expectedStatus, ok) - }) - } -} - func TestContainerNamesConfiguredForMultipleInstrumentations(t *testing.T) { tests := []struct { name string @@ -5661,8 +4834,8 @@ func TestContainerNamesConfiguredForMultipleInstrumentations(t *testing.T) { { name: "Multiple instrumentations enabled with containers", instrumentations: languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "java"}, - NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "nodejs"}, + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"java"}}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"nodejs"}}, }, expectedStatus: true, expectedMsg: nil, @@ -5679,7 +4852,7 @@ func TestContainerNamesConfiguredForMultipleInstrumentations(t *testing.T) { { name: "Multiple instrumentations enabled with containers for single instrumentation", instrumentations: languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "test"}, + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"test"}}, NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, }, expectedStatus: false, @@ -5696,8 +4869,8 @@ func TestContainerNamesConfiguredForMultipleInstrumentations(t *testing.T) { { name: "Multiple instrumentations enabled with duplicated containers", instrumentations: languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "app,app1,java"}, - NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "app1,app,nodejs"}, + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"app", "app1", "java"}}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"app1", "app", "nodejs"}}, }, expectedStatus: false, expectedMsg: fmt.Errorf("duplicated container names detected: [app app1]"), @@ -5705,8 +4878,8 @@ func TestContainerNamesConfiguredForMultipleInstrumentations(t *testing.T) { { name: "Multiple instrumentations enabled with duplicated containers for single instrumentation", instrumentations: languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "app,app,java"}, - NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "nodejs"}, + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"app", "app", "java"}}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"nodejs"}}, }, expectedStatus: false, expectedMsg: fmt.Errorf("duplicated container names detected: [app]"), @@ -5715,7 +4888,7 @@ func TestContainerNamesConfiguredForMultipleInstrumentations(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - ok, msg := test.instrumentations.areContainerNamesConfiguredForMultipleInstrumentations() + ok, msg := test.instrumentations.areInstrumentedContainersCorrect() assert.Equal(t, test.expectedStatus, ok) assert.Equal(t, test.expectedMsg, msg) }) @@ -5727,6 +4900,8 @@ func TestInstrumentationLanguageContainersSet(t *testing.T) { name string instrumentations languageInstrumentations containers string + pod corev1.Pod + ns corev1.Namespace expectedInstrumentations languageInstrumentations }{ { @@ -5735,23 +4910,37 @@ func TestInstrumentationLanguageContainersSet(t *testing.T) { NodeJS: instrumentationWithContainers{Instrumentation: nil}, Python: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, }, - containers: "python,python1", + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectContainerName: "python,python1", + }, + }, + }, + ns: corev1.Namespace{}, expectedInstrumentations: languageInstrumentations{ NodeJS: instrumentationWithContainers{Instrumentation: nil}, - Python: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "python,python1"}, + Python: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: []string{"python", "python1"}}, }, }, { - name: "Set containers when all instrumentations disabled", - instrumentations: languageInstrumentations{}, - containers: "python,python1", + name: "Set containers when all instrumentations disabled", + instrumentations: languageInstrumentations{}, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectContainerName: "python,python1", + }, + }, + }, expectedInstrumentations: languageInstrumentations{}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - test.instrumentations.setInstrumentationLanguageContainers(test.containers) + err := test.instrumentations.setCommonInstrumentedContainers(test.ns, test.pod) + assert.NoError(t, err) assert.Equal(t, test.expectedInstrumentations, test.instrumentations) }) } diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index 16e1cbf9fe..c9ae3fb09b 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -62,9 +62,11 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations var err error i.logger.V(1).Info("injecting Java instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - javaContainers := insts.Java.Containers + if len(insts.Java.Containers) == 0 { + insts.Java.Containers = []string{pod.Spec.Containers[0].Name} + } - for _, container := range strings.Split(javaContainers, ",") { + for _, container := range insts.Java.Containers { index := getContainerIndex(container, pod) pod, err = injectJavaagent(otelinst.Spec.Java, pod, index) if err != nil { @@ -81,9 +83,11 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations var err error i.logger.V(1).Info("injecting NodeJS instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - nodejsContainers := insts.NodeJS.Containers + if len(insts.NodeJS.Containers) == 0 { + insts.NodeJS.Containers = []string{pod.Spec.Containers[0].Name} + } - for _, container := range strings.Split(nodejsContainers, ",") { + for _, container := range insts.NodeJS.Containers { index := getContainerIndex(container, pod) pod, err = injectNodeJSSDK(otelinst.Spec.NodeJS, pod, index) if err != nil { @@ -100,9 +104,11 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations var err error i.logger.V(1).Info("injecting Python instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - pythonContainers := insts.Python.Containers + if len(insts.Python.Containers) == 0 { + insts.Python.Containers = []string{pod.Spec.Containers[0].Name} + } - for _, container := range strings.Split(pythonContainers, ",") { + for _, container := range insts.Python.Containers { index := getContainerIndex(container, pod) pod, err = injectPythonSDK(otelinst.Spec.Python, pod, index) if err != nil { @@ -119,9 +125,11 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations var err error i.logger.V(1).Info("injecting DotNet instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - dotnetContainers := insts.DotNet.Containers + if len(insts.DotNet.Containers) == 0 { + insts.DotNet.Containers = []string{pod.Spec.Containers[0].Name} + } - for _, container := range strings.Split(dotnetContainers, ",") { + for _, container := range insts.DotNet.Containers { index := getContainerIndex(container, pod) pod, err = injectDotNetSDK(otelinst.Spec.DotNet, pod, index, insts.DotNet.AdditionalAnnotations[annotationDotNetRuntime]) if err != nil { @@ -139,10 +147,12 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations var err error i.logger.V(1).Info("injecting Go instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - goContainers := insts.Go.Containers + if len(insts.Go.Containers) == 0 { + insts.Go.Containers = []string{pod.Spec.Containers[0].Name} + } // Go instrumentation supports only single container instrumentation. - index := getContainerIndex(goContainers, pod) + index := getContainerIndex(insts.Go.Containers[0], pod) pod, err = injectGoSDK(otelinst.Spec.Go, pod, cfg) if err != nil { i.logger.Info("Skipping Go SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) @@ -163,9 +173,11 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations otelinst := *insts.ApacheHttpd.Instrumentation i.logger.V(1).Info("injecting Apache Httpd instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - apacheHttpdContainers := insts.ApacheHttpd.Containers + if len(insts.ApacheHttpd.Containers) == 0 { + insts.ApacheHttpd.Containers = []string{pod.Spec.Containers[0].Name} + } - for _, container := range strings.Split(apacheHttpdContainers, ",") { + for _, container := range insts.ApacheHttpd.Containers { index := getContainerIndex(container, pod) // Apache agent is configured via config files rather than env vars. // Therefore, service name, otlp endpoint and other attributes are passed to the agent injection method @@ -181,9 +193,11 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations otelinst := *insts.Nginx.Instrumentation i.logger.V(1).Info("injecting Nginx instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - nginxContainers := insts.Nginx.Containers + if len(insts.Nginx.Containers) == 0 { + insts.Nginx.Containers = []string{pod.Spec.Containers[0].Name} + } - for _, container := range strings.Split(nginxContainers, ",") { + for _, container := range insts.Nginx.Containers { index := getContainerIndex(container, pod) // Nginx agent is configured via config files rather than env vars. // Therefore, service name, otlp endpoint and other attributes are passed to the agent injection method @@ -197,9 +211,11 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations otelinst := *insts.Sdk.Instrumentation i.logger.V(1).Info("injecting sdk-only instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - sdkContainers := insts.Sdk.Containers + if len(insts.Sdk.Containers) == 0 { + insts.Sdk.Containers = []string{pod.Spec.Containers[0].Name} + } - for _, container := range strings.Split(sdkContainers, ",") { + for _, container := range insts.Sdk.Containers { index := getContainerIndex(container, pod) pod = i.injectCommonEnvVar(otelinst, pod, index) pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index 84859d3bc1..393f9e9fe2 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -650,7 +650,10 @@ func TestInjectJava(t *testing.T) { }, } insts := languageInstrumentations{ - Java: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, + Java: instrumentationWithContainers{ + Instrumentation: &inst, + Containers: []string{"app"}, + }, } inj := sdkInjector{ logger: logr.Discard(), @@ -771,7 +774,10 @@ func TestInjectNodeJS(t *testing.T) { }, } insts := languageInstrumentations{ - NodeJS: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, + NodeJS: instrumentationWithContainers{ + Instrumentation: &inst, + Containers: []string{"app"}, + }, } inj := sdkInjector{ logger: logr.Discard(), @@ -891,7 +897,10 @@ func TestInjectPython(t *testing.T) { }, } insts := languageInstrumentations{ - Python: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, + Python: instrumentationWithContainers{ + Instrumentation: &inst, + Containers: []string{"app"}, + }, } inj := sdkInjector{ @@ -1023,7 +1032,10 @@ func TestInjectDotNet(t *testing.T) { }, } insts := languageInstrumentations{ - DotNet: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, + DotNet: instrumentationWithContainers{ + Instrumentation: &inst, + Containers: []string{"app"}, + }, } inj := sdkInjector{ logger: logr.Discard(), @@ -1169,14 +1181,16 @@ func TestInjectGo(t *testing.T) { { name: "shared process namespace disabled", insts: languageInstrumentations{ - Go: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{ - Spec: v1alpha1.InstrumentationSpec{ - Go: v1alpha1.Go{ - Image: "otel/go:1", + Go: instrumentationWithContainers{ + Containers: []string{"app"}, + Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + }, }, }, }, - }, }, pod: corev1.Pod{ Spec: corev1.PodSpec{ @@ -1202,14 +1216,16 @@ func TestInjectGo(t *testing.T) { { name: "OTEL_GO_AUTO_TARGET_EXE not set", insts: languageInstrumentations{ - Go: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{ - Spec: v1alpha1.InstrumentationSpec{ - Go: v1alpha1.Go{ - Image: "otel/go:1", + Go: instrumentationWithContainers{ + Containers: []string{"app"}, + Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + }, }, }, }, - }, }, pod: corev1.Pod{ Spec: corev1.PodSpec{ @@ -1233,20 +1249,22 @@ func TestInjectGo(t *testing.T) { { name: "OTEL_GO_AUTO_TARGET_EXE set by inst", insts: languageInstrumentations{ - Go: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{ - Spec: v1alpha1.InstrumentationSpec{ - Go: v1alpha1.Go{ - Image: "otel/go:1", - Env: []corev1.EnvVar{ - { - Name: "OTEL_GO_AUTO_TARGET_EXE", - Value: "foo", + Go: instrumentationWithContainers{ + Containers: []string{"app"}, + Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "foo", + }, }, }, }, }, }, - }, }, pod: corev1.Pod{ Spec: corev1.PodSpec{ @@ -1345,7 +1363,7 @@ func TestInjectGo(t *testing.T) { name: "OTEL_GO_AUTO_TARGET_EXE set by annotation", insts: languageInstrumentations{ Go: instrumentationWithContainers{ - Containers: "", + Containers: []string{"app"}, Instrumentation: &v1alpha1.Instrumentation{ Spec: v1alpha1.InstrumentationSpec{ Go: v1alpha1.Go{ @@ -1494,7 +1512,7 @@ func TestInjectApacheHttpd(t *testing.T) { }, }, }, - Containers: "", + Containers: []string{"app"}, }, }, pod: corev1.Pod{ @@ -1672,7 +1690,7 @@ func TestInjectNginx(t *testing.T) { }, }, }, - Containers: "", + Containers: []string{"app"}, }, }, pod: corev1.Pod{ @@ -1841,7 +1859,7 @@ func TestInjectSdkOnly(t *testing.T) { }, } insts := languageInstrumentations{ - Sdk: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, + Sdk: instrumentationWithContainers{Instrumentation: &inst, Containers: []string{"app"}}, } inj := sdkInjector{ diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml index 89ad0aa61e..ce304aa78a 100644 --- a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: Pod metadata: annotations: - instrumentation.opentelemetry.io/container-names: shouldnt-be-instrumented instrumentation.opentelemetry.io/dotnet-container-names: dotnetapp instrumentation.opentelemetry.io/inject-dotnet: "true" instrumentation.opentelemetry.io/inject-java: "true" @@ -195,13 +194,6 @@ spec: readOnly: true - mountPath: /otel-auto-instrumentation-python name: opentelemetry-auto-instrumentation-python - - env: - - name: TEST - value: test - name: shouldnt-be-instrumented - volumeMounts: - - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - readOnly: true - args: - --feature-gates=-component.UseLocalHostAsDefaultHost - --config=env:OTEL_CONFIG @@ -248,9 +240,6 @@ status: - name: pythonapp ready: true started: true - - name: shouldnt-be-instrumented - ready: true - started: true initContainerStatuses: - name: opentelemetry-auto-instrumentation-java ready: true diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml index 32f0b4658c..88d8f7a564 100644 --- a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml @@ -12,7 +12,6 @@ spec: labels: app: pod-with-multi-instrumentation annotations: - instrumentation.opentelemetry.io/container-names: "shouldnt-be-instrumented" instrumentation.opentelemetry.io/dotnet-container-names: "dotnetapp" instrumentation.opentelemetry.io/inject-dotnet: "true" instrumentation.opentelemetry.io/inject-java: "true" @@ -59,12 +58,4 @@ spec: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] - - name: shouldnt-be-instrumented - image: rabbitmq:3 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - env: - - name: TEST - value: test + diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-assert.yaml new file mode 100644 index 0000000000..5c9ccd6f37 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-assert.yaml @@ -0,0 +1,82 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/container-names: "dotnetapp" + instrumentation.opentelemetry.io/inject-dotnet: "true" + sidecar.opentelemetry.io/inject: "true" + labels: + app: pod-with-multi-instrumentation2 +spec: + containers: + - env: + - name: OTEL_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: OTEL_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: ASPNETCORE_URLS + value: http://+:8083 + - name: OTEL_SERVICE_NAME + value: dotnetapp + - name: CORECLR_ENABLE_PROFILING + value: "1" + - name: CORECLR_PROFILER + value: '{918728DD-259F-4A6A-AC2B-B85E1B658318}' + - name: CORECLR_PROFILER_PATH + value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so + - name: DOTNET_STARTUP_HOOKS + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll + - name: DOTNET_ADDITIONAL_DEPS + value: /otel-auto-instrumentation-dotnet/AdditionalDeps + - name: OTEL_DOTNET_AUTO_HOME + value: /otel-auto-instrumentation-dotnet + - name: DOTNET_SHARED_STORE + value: /otel-auto-instrumentation-dotnet/store + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + name: dotnetapp + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + readOnly: true + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet + - name: javaapp + - name: otc-container + initContainers: + - name: opentelemetry-auto-instrumentation-dotnet +status: + containerStatuses: + - name: dotnetapp + ready: true + started: true + - name: javaapp + ready: true + started: true + - name: otc-container + ready: true + started: true + initContainerStatuses: + - name: opentelemetry-auto-instrumentation-dotnet + ready: true + phase: Running diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-error.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-error.yaml new file mode 100644 index 0000000000..9be0c047f1 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-error.yaml @@ -0,0 +1,114 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/container-names: "dotnetapp" + instrumentation.opentelemetry.io/inject-dotnet: "true" + sidecar.opentelemetry.io/inject: "true" + labels: + app: pod-with-multi-instrumentation2 +spec: + containers: + - env: + - name: OTEL_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: OTEL_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: ASPNETCORE_URLS + value: http://+:8083 + - name: OTEL_SERVICE_NAME + value: dotnetapp + - name: CORECLR_ENABLE_PROFILING + value: "1" + - name: CORECLR_PROFILER + value: '{918728DD-259F-4A6A-AC2B-B85E1B658318}' + - name: CORECLR_PROFILER_PATH + value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so + - name: DOTNET_STARTUP_HOOKS + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll + - name: DOTNET_ADDITIONAL_DEPS + value: /otel-auto-instrumentation-dotnet/AdditionalDeps + - name: OTEL_DOTNET_AUTO_HOME + value: /otel-auto-instrumentation-dotnet + - name: DOTNET_SHARED_STORE + value: /otel-auto-instrumentation-dotnet/store + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + name: dotnetapp + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + readOnly: true + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet + - env: + - name: OTEL_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: OTEL_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: OTEL_SERVICE_NAME + value: javaapp + - name: JAVA_TOOL_OPTIONS + value: ' -javaagent:/otel-auto-instrumentation-java/javaagent.jar' + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + name: javaapp + - name: otc-container + initContainers: + - name: opentelemetry-auto-instrumentation-dotnet +status: + containerStatuses: + - name: dotnetapp + ready: true + started: true + - name: javaapp + ready: true + started: true + - name: otc-container + ready: true + started: true + initContainerStatuses: + - name: opentelemetry-auto-instrumentation-dotnet + ready: true + phase: Running diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-install-app.yaml new file mode 100644 index 0000000000..f203cf0b8e --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/02-install-app.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment-with-multi-instrumentation2 +spec: + selector: + matchLabels: + app: pod-with-multi-instrumentation2 + replicas: 1 + template: + metadata: + labels: + app: pod-with-multi-instrumentation2 + annotations: + instrumentation.opentelemetry.io/container-names: "dotnetapp" + instrumentation.opentelemetry.io/inject-dotnet: "true" + sidecar.opentelemetry.io/inject: "true" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - name: dotnetapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-dotnet:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + env: + - name: ASPNETCORE_URLS + value: "http://+:8083" + - name: javaapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"]