pkg/instrumentation/dotnet.go (132 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package instrumentation import ( "errors" "fmt" corev1 "k8s.io/api/core/v1" "github.com/aws/amazon-cloudwatch-agent-operator/apis/v1alpha1" ) const ( envDotNetCoreClrEnableProfiling = "CORECLR_ENABLE_PROFILING" envDotNetCoreClrProfiler = "CORECLR_PROFILER" envDotNetCoreClrProfilerPath = "CORECLR_PROFILER_PATH" envDotNetAdditionalDeps = "DOTNET_ADDITIONAL_DEPS" envDotNetSharedStore = "DOTNET_SHARED_STORE" envDotNetStartupHook = "DOTNET_STARTUP_HOOKS" envDotNetOTelAutoHome = "OTEL_DOTNET_AUTO_HOME" dotNetCoreClrEnableProfilingEnabled = "1" dotNetCoreClrProfilerID = "{918728DD-259F-4A6A-AC2B-B85E1B658318}" dotNetCoreClrProfilerGlibcPath = "/otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so" dotNetCoreClrProfilerMuslPath = "/otel-auto-instrumentation-dotnet/linux-musl-x64/OpenTelemetry.AutoInstrumentation.Native.so" dotNetAdditionalDepsPath = "/otel-auto-instrumentation-dotnet/AdditionalDeps" dotNetOTelAutoHomePath = "/otel-auto-instrumentation-dotnet" dotNetSharedStorePath = "/otel-auto-instrumentation-dotnet/store" dotNetStartupHookPath = "/otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll" dotNetAutoPlugins = "AWS.Distro.OpenTelemetry.AutoInstrumentation.Plugin, AWS.Distro.OpenTelemetry.AutoInstrumentation" dotnetVolumeName = volumeName + "-dotnet" dotnetInitContainerName = initContainerName + "-dotnet" dotnetInstrMountPath = "/otel-auto-instrumentation-dotnet" ) const ( dotNetCoreClrProfilerPathWindows = "C:\\otel-auto-instrumentation-dotnet\\win-x64\\OpenTelemetry.AutoInstrumentation.Native.dll" dotNetAdditionalDepsPathWindows = "C:\\otel-auto-instrumentation-dotnet\\AdditionalDeps" dotNetOTelAutoHomePathWindows = "C:\\otel-auto-instrumentation-dotnet" dotNetSharedStorePathWindows = "C:\\otel-auto-instrumentation-dotnet\\store" dotNetStartupHookPathWindows = "C:\\otel-auto-instrumentation-dotnet\\net\\OpenTelemetry.AutoInstrumentation.StartupHook.dll" dotnetInstrMountPathWindows = "\\otel-auto-instrumentation-dotnet" ) // Supported .NET runtime identifiers (https://learn.microsoft.com/en-us/dotnet/core/rid-catalog), can be set by instrumentation.opentelemetry.io/inject-dotnet. const ( dotNetRuntimeLinuxGlibc = "linux-x64" dotNetRuntimeLinuxMusl = "linux-musl-x64" ) var ( dotNetCommandLinux = []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath} dotNetCommandWindows = []string{"CMD", "/c", "xcopy", "/e", "autoinstrumentation\\*", dotnetInstrMountPathWindows} ) func injectDotNetSDK(dotNetSpec v1alpha1.DotNet, pod corev1.Pod, index int, runtime string) (corev1.Pod, error) { // caller checks if there is at least one container. container := &pod.Spec.Containers[index] err := validateContainerEnv(container.Env, envDotNetStartupHook, envDotNetAdditionalDeps, envDotNetSharedStore) if err != nil { return pod, err } // check if OTEL_DOTNET_AUTO_HOME env var is already set in the container // if it is already set, then we assume that .NET Auto-instrumentation is already configured for this container if getIndexOfEnv(container.Env, envDotNetOTelAutoHome) > -1 { return pod, errors.New("OTEL_DOTNET_AUTO_HOME environment variable is already set in the container") } // check if OTEL_DOTNET_AUTO_HOME env var is already set in the .NET instrumentation spec // if it is already set, then we assume that .NET Auto-instrumentation is already configured for this container if getIndexOfEnv(dotNetSpec.Env, envDotNetOTelAutoHome) > -1 { return pod, errors.New("OTEL_DOTNET_AUTO_HOME environment variable is already set in the .NET instrumentation spec") } coreClrProfilerPath := "" switch runtime { case "", dotNetRuntimeLinuxGlibc: coreClrProfilerPath = dotNetCoreClrProfilerGlibcPath case dotNetRuntimeLinuxMusl: coreClrProfilerPath = dotNetCoreClrProfilerMuslPath default: return pod, fmt.Errorf("provided instrumentation.opentelemetry.io/dotnet-runtime annotation value '%s' is not supported", runtime) } // inject .NET instrumentation spec env vars. for _, env := range dotNetSpec.Env { idx := getIndexOfEnv(container.Env, env.Name) if idx == -1 { container.Env = append(container.Env, env) } } const ( doNotConcatEnvValues = false concatEnvValues = true ) setDotNetEnvVar(container, envDotNetCoreClrEnableProfiling, dotNetCoreClrEnableProfilingEnabled, doNotConcatEnvValues) setDotNetEnvVar(container, envDotNetCoreClrProfiler, dotNetCoreClrProfilerID, doNotConcatEnvValues) if isWindowsPod(pod) { setDotNetEnvVar(container, envDotNetCoreClrProfilerPath, dotNetCoreClrProfilerPathWindows, doNotConcatEnvValues) setDotNetEnvVar(container, envDotNetStartupHook, dotNetStartupHookPathWindows, concatEnvValues) setDotNetEnvVar(container, envDotNetAdditionalDeps, dotNetAdditionalDepsPathWindows, concatEnvValues) setDotNetEnvVar(container, envDotNetOTelAutoHome, dotNetOTelAutoHomePathWindows, doNotConcatEnvValues) setDotNetEnvVar(container, envDotNetSharedStore, dotNetSharedStorePathWindows, concatEnvValues) } else { setDotNetEnvVar(container, envDotNetCoreClrProfilerPath, coreClrProfilerPath, doNotConcatEnvValues) setDotNetEnvVar(container, envDotNetStartupHook, dotNetStartupHookPath, concatEnvValues) setDotNetEnvVar(container, envDotNetAdditionalDeps, dotNetAdditionalDepsPath, concatEnvValues) setDotNetEnvVar(container, envDotNetOTelAutoHome, dotNetOTelAutoHomePath, doNotConcatEnvValues) setDotNetEnvVar(container, envDotNetSharedStore, dotNetSharedStorePath, concatEnvValues) } container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ Name: dotnetVolumeName, MountPath: dotnetInstrMountPath, }) // We just inject Volumes and init containers for the first processed container. if isInitContainerMissing(pod, dotnetInitContainerName) { pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ Name: dotnetVolumeName, VolumeSource: corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{ SizeLimit: volumeSize(dotNetSpec.VolumeSizeLimit), }, }}) command := dotNetCommandLinux if isWindowsPod(pod) { command = dotNetCommandWindows } pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ Name: dotnetInitContainerName, Image: dotNetSpec.Image, Command: command, Resources: dotNetSpec.Resources, VolumeMounts: []corev1.VolumeMount{{ Name: dotnetVolumeName, MountPath: dotnetInstrMountPath, }}, }) } return pod, nil } // setDotNetEnvVar function sets env var to the container if not exist already. // value of concatValues should be set to true if the env var supports multiple values separated by :. // If it is set to false, the original container's env var value has priority. func setDotNetEnvVar(container *corev1.Container, envVarName string, envVarValue string, concatValues bool) { idx := getIndexOfEnv(container.Env, envVarName) if idx < 0 { container.Env = append(container.Env, corev1.EnvVar{ Name: envVarName, Value: envVarValue, }) return } if concatValues { container.Env[idx].Value = fmt.Sprintf("%s:%s", container.Env[idx].Value, envVarValue) } }