Skip to content

Commit

Permalink
.NET - enable bytecode instrumentation (open-telemetry#1081)
Browse files Browse the repository at this point in the history
* .NET Auto Instrumentation - support for byte code instrumentation

* extract method to set env variable

* code review feedback
  • Loading branch information
Kielek committed Sep 15, 2022
1 parent 8a9f315 commit 9164ecd
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 55 deletions.
4 changes: 4 additions & 0 deletions autoinstrumentation/dotnet/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
# one init container will be created to copy the files to your app's container.
# - Grant the necessary access to the files in the `/autoinstrumentation` directory.
# - Following environment variables are injected to the application container to enable the auto-instrumentation.
# CORECLR_ENABLE_PROFILING=1
# CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658318}
# CORECLR_PROFILER_PATH=%InstallationLocation%/OpenTelemetry.AutoInstrumentation.Native.so
# DOTNET_ADDITIONAL_DEPS=%InstallationLocation%/AdditionalDeps
# DOTNET_SHARED_STORE=%InstallationLocation%/store
# DOTNET_STARTUP_HOOKS=%InstallationLocation%/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll
# OTEL_DOTNET_AUTO_HOME=%InstallationLocation%

FROM busybox

Expand Down
109 changes: 64 additions & 45 deletions pkg/instrumentation/dotnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ import (
)

const (
envDotNetAdditionalDeps = "DOTNET_ADDITIONAL_DEPS"
envDotNetSharedStore = "DOTNET_SHARED_STORE"
envDotNetStartupHook = "DOTNET_STARTUP_HOOKS"
dotNetAdditionalDepsPath = "./otel-auto-instrumentation/AdditionalDeps"
dotNetSharedStorePath = "/otel-auto-instrumentation/store"
dotNetStartupHookPath = "/otel-auto-instrumentation/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll"
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}"
dotNetCoreClrProfilerPath = "/otel-auto-instrumentation/OpenTelemetry.AutoInstrumentation.Native.so"
dotNetAdditionalDepsPath = "/otel-auto-instrumentation/AdditionalDeps"
dotNetOTelAutoHomePath = "/otel-auto-instrumentation"
dotNetSharedStorePath = "/otel-auto-instrumentation/store"
dotNetStartupHookPath = "/otel-auto-instrumentation/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll"
)

func injectDotNetSDK(logger logr.Logger, dotNetSpec v1alpha1.DotNet, pod corev1.Pod, index int) corev1.Pod {
Expand All @@ -44,49 +52,37 @@ func injectDotNetSDK(logger logr.Logger, dotNetSpec v1alpha1.DotNet, pod corev1.
}
}

idx := getIndexOfEnv(container.Env, envDotNetStartupHook)
if idx == -1 {
container.Env = append(container.Env, corev1.EnvVar{
Name: envDotNetStartupHook,
Value: dotNetStartupHookPath,
})
} else if idx > -1 {
if container.Env[idx].ValueFrom != nil {
// TODO add to status object or submit it as an event
logger.Info("Skipping DotNet SDK injection, the container defines DOTNET_STARTUP_HOOKS env var value via ValueFrom", "container", container.Name)
return pod
}
container.Env[idx].Value = fmt.Sprintf("%s:%s", container.Env[idx].Value, dotNetStartupHookPath)
const (
doNotConcatEnvValues = false
concatEnvValues = true
)

if !trySetEnvVar(logger, &container, envDotNetCoreClrEnableProfiling, dotNetCoreClrEnableProfilingEnabled, doNotConcatEnvValues) {
return pod
}

idx = getIndexOfEnv(container.Env, envDotNetAdditionalDeps)
if idx == -1 {
container.Env = append(container.Env, corev1.EnvVar{
Name: envDotNetAdditionalDeps,
Value: dotNetAdditionalDepsPath,
})
} else if idx > -1 {
if container.Env[idx].ValueFrom != nil {
// TODO add to status object or submit it as an event
logger.Info("Skipping DotNet SDK injection, the container defines DOTNET_ADDITIONAL_DEPS env var value via ValueFrom", "container", container.Name)
return pod
}
container.Env[idx].Value = fmt.Sprintf("%s:%s", container.Env[idx].Value, dotNetAdditionalDepsPath)
if !trySetEnvVar(logger, &container, envDotNetCoreClrProfiler, dotNetCoreClrProfilerId, doNotConcatEnvValues) {
return pod
}

idx = getIndexOfEnv(container.Env, envDotNetSharedStore)
if idx == -1 {
container.Env = append(container.Env, corev1.EnvVar{
Name: envDotNetSharedStore,
Value: dotNetSharedStorePath,
})
} else if idx > -1 {
if container.Env[idx].ValueFrom != nil {
// TODO add to status object or submit it as an event
logger.Info("Skipping DotNet SDK injection, the container defines DOTNET_SHARED_STORE env var value via ValueFrom", "container", container.Name)
return pod
}
container.Env[idx].Value = fmt.Sprintf("%s:%s", container.Env[idx].Value, dotNetSharedStorePath)
if !trySetEnvVar(logger, &container, envDotNetCoreClrProfilerPath, dotNetCoreClrProfilerPath, doNotConcatEnvValues) {
return pod
}

if !trySetEnvVar(logger, &container, envDotNetStartupHook, dotNetStartupHookPath, concatEnvValues) {
return pod
}

if !trySetEnvVar(logger, &container, envDotNetAdditionalDeps, dotNetAdditionalDepsPath, concatEnvValues) {
return pod
}

if !trySetEnvVar(logger, &container, envDotNetOTelAutoHome, dotNetOTelAutoHomePath, doNotConcatEnvValues) {
return pod
}

if !trySetEnvVar(logger, &container, envDotNetSharedStore, dotNetSharedStorePath, concatEnvValues) {
return pod
}

container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{
Expand Down Expand Up @@ -116,3 +112,26 @@ func injectDotNetSDK(logger logr.Logger, dotNetSpec v1alpha1.DotNet, pod corev1.
pod.Spec.Containers[index] = container
return pod
}

func trySetEnvVar(logger logr.Logger, container *corev1.Container, envVarName string, envVarValue string, concatValues bool) bool {
idx := getIndexOfEnv(container.Env, envVarName)
if idx < 0 {
container.Env = append(container.Env, corev1.EnvVar{
Name: envVarName,
Value: envVarValue,
})
return true
}

if container.Env[idx].ValueFrom != nil {
// TODO add to status object or submit it as an event
logger.Info("Skipping DotNet SDK injection, the container defines env var value via ValueFrom", "envVar", envVarName, "container", container.Name)
return false
}

if concatValues {
container.Env[idx].Value = fmt.Sprintf("%s:%s", container.Env[idx].Value, envVarValue)
}

return true
}
180 changes: 178 additions & 2 deletions pkg/instrumentation/dotnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestInjectDotNetSDK(t *testing.T) {
expected corev1.Pod
}{
{
name: "DOTNET_STARTUP_HOOKS, DOTNET_SHARED_STORE, DOTNET_ADDITIONAL_DEPS not defined",
name: "CORECLR_ENABLE_PROFILING, CORECLR_PROFILER, CORECLR_PROFILER_PATH, DOTNET_STARTUP_HOOKS, DOTNET_SHARED_STORE, DOTNET_ADDITIONAL_DEPS, OTEL_DOTNET_AUTO_HOME not defined",
DotNet: v1alpha1.DotNet{Image: "foo/bar:1", Env: []corev1.EnvVar{}},
pod: corev1.Pod{
Spec: corev1.PodSpec{
Expand Down Expand Up @@ -72,6 +72,18 @@ func TestInjectDotNetSDK(t *testing.T) {
},
},
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrEnableProfiling,
Value: dotNetCoreClrEnableProfilingEnabled,
},
{
Name: envDotNetCoreClrProfiler,
Value: dotNetCoreClrProfilerId,
},
{
Name: envDotNetCoreClrProfilerPath,
Value: dotNetCoreClrProfilerPath,
},
{
Name: envDotNetStartupHook,
Value: dotNetStartupHookPath,
Expand All @@ -80,6 +92,10 @@ func TestInjectDotNetSDK(t *testing.T) {
Name: envDotNetAdditionalDeps,
Value: dotNetAdditionalDepsPath,
},
{
Name: envDotNetOTelAutoHome,
Value: dotNetOTelAutoHomePath,
},
{
Name: envDotNetSharedStore,
Value: dotNetSharedStorePath,
Expand All @@ -91,13 +107,25 @@ func TestInjectDotNetSDK(t *testing.T) {
},
},
{
name: "DOTNET_STARTUP_HOOKS, DOTNET_ADDITIONAL_DEPS, DOTNET_SHARED_STORE defined",
name: "CORECLR_ENABLE_PROFILING, CORECLR_PROFILER, CORECLR_PROFILER_PATH, DOTNET_STARTUP_HOOKS, DOTNET_ADDITIONAL_DEPS, DOTNET_SHARED_STORE, OTEL_DOTNET_AUTO_HOME defined",
DotNet: v1alpha1.DotNet{Image: "foo/bar:1"},
pod: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrEnableProfiling,
Value: "/foo:/bar",
},
{
Name: envDotNetCoreClrProfiler,
Value: "/foo:/bar",
},
{
Name: envDotNetCoreClrProfilerPath,
Value: "/foo:/bar",
},
{
Name: envDotNetStartupHook,
Value: "/foo:/bar",
Expand All @@ -110,6 +138,10 @@ func TestInjectDotNetSDK(t *testing.T) {
Name: envDotNetSharedStore,
Value: "/foo:/bar",
},
{
Name: envDotNetOTelAutoHome,
Value: "/foo:/bar",
},
},
},
},
Expand Down Expand Up @@ -145,6 +177,18 @@ func TestInjectDotNetSDK(t *testing.T) {
},
},
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrEnableProfiling,
Value: "/foo:/bar",
},
{
Name: envDotNetCoreClrProfiler,
Value: "/foo:/bar",
},
{
Name: envDotNetCoreClrProfilerPath,
Value: "/foo:/bar",
},
{
Name: envDotNetStartupHook,
Value: fmt.Sprintf("%s:%s", "/foo:/bar", dotNetStartupHookPath),
Expand All @@ -157,6 +201,106 @@ func TestInjectDotNetSDK(t *testing.T) {
Name: envDotNetSharedStore,
Value: fmt.Sprintf("%s:%s", "/foo:/bar", dotNetSharedStorePath),
},
{
Name: envDotNetOTelAutoHome,
Value: "/foo:/bar",
},
},
},
},
},
},
},
{
name: "CORECLR_ENABLE_PROFILING defined as ValueFrom",
DotNet: v1alpha1.DotNet{Image: "foo/bar:1"},
pod: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrEnableProfiling,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
},
},
expected: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrEnableProfiling,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
},
},
},
{
name: "CORECLR_PROFILER defined as ValueFrom",
DotNet: v1alpha1.DotNet{Image: "foo/bar:1"},
pod: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrProfiler,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
},
},
expected: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrProfiler,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
},
},
},
{
name: "CORECLR_PROFILER_PATH defined as ValueFrom",
DotNet: v1alpha1.DotNet{Image: "foo/bar:1"},
pod: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrProfilerPath,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
},
},
expected: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetCoreClrProfilerPath,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
Expand Down Expand Up @@ -259,6 +403,38 @@ func TestInjectDotNetSDK(t *testing.T) {
},
},
},
{
name: "OTEL_DOTNET_AUTO_HOME defined as ValueFrom",
DotNet: v1alpha1.DotNet{Image: "foo/bar:1"},
pod: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetOTelAutoHome,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
},
},
expected: corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: envDotNetOTelAutoHome,
ValueFrom: &corev1.EnvVarSource{},
},
},
},
},
},
},
},
}

for _, test := range tests {
Expand Down
Loading

0 comments on commit 9164ecd

Please sign in to comment.