From 6e2ce09829390fb5541c98b59c70493aaae00152 Mon Sep 17 00:00:00 2001 From: "dd-octo-sts[bot]" <200755185+dd-octo-sts[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:57:19 +0000 Subject: [PATCH] fix(cluster-agent): Guard against re-admission for APM auto-instrumentation in image_volume mode (#46743) ### What does this PR do? Avoid double-injection by returning early if the pod already has image_volume mode's init containers. Init_container mode was already guarded by checking for per-language init containers (e.g. datadog-lib-python-init). This change adds the same style of guard for image_volume mode by checking for the datadog-apm-inject-preload init container. ### Motivation The webhook may be run twice, but we do not want to inject twice. CSI mode needs a guard in the future as well. ### Describe how you validated your changes Tests in target_mutator_test.go were added for both re-admission cases: one for init_container mode and one for image_volume mode. The test asserts that the pod is not changed ("mutated") at all in the case that the representative init container(s) are present. ### Additional Notes Co-authored-by: mikayla.toffler (cherry picked from commit 2d33331c4846674fc476984c4e20f2d5ab5235e3) ___ Co-authored-by: Mikayla Toffler <46911781+mtoffl01@users.noreply.github.com> --- .../autoinstrumentation/target_mutator.go | 7 +++++ .../target_mutator_test.go | 29 +++++++++++++++++++ ...admission-ssi-imgvol-e9dfef7c4b5b8b0d.yaml | 3 ++ 3 files changed, 39 insertions(+) create mode 100644 releasenotes/notes/guard-readmission-ssi-imgvol-e9dfef7c4b5b8b0d.yaml diff --git a/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator.go b/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator.go index 05d9caba9358b5..46b40f94501174 100644 --- a/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator.go +++ b/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator.go @@ -23,6 +23,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/metrics" "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/autoinstrumentation/annotation" "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/autoinstrumentation/imageresolver" + "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/autoinstrumentation/libraryinjection" mutatecommon "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/common" "github.com/DataDog/datadog-agent/pkg/util/log" ) @@ -185,12 +186,18 @@ func (m *TargetMutator) MutatePod(pod *corev1.Pod, ns string, _ dynamic.Interfac log.Debugf("Mutating pod in target mutator %q", mutatecommon.PodString(pod)) // The admission can be re-run for the same pod. Fast return if we injected the library already. + // Check for the init_container mode's per-language init containers. for _, lang := range supportedLanguages { if containsInitContainer(pod, initContainerName(lang)) { log.Debugf("Init container %q already exists in pod %q", initContainerName(lang), mutatecommon.PodString(pod)) return false, nil } } + // Check for the image_volume mode's init container. + if containsInitContainer(pod, libraryinjection.InjectLDPreloadInitContainerName) { + log.Debugf("Init container %q already exists in pod %q", libraryinjection.InjectLDPreloadInitContainerName, mutatecommon.PodString(pod)) + return false, nil + } // Get the target to inject. If there is not target, we should not mutate the pod. target := m.getTarget(pod) diff --git a/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator_test.go b/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator_test.go index 1565855a41077e..839d5bd6c6f12c 100644 --- a/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator_test.go +++ b/pkg/clusteragent/admission/mutate/autoinstrumentation/target_mutator_test.go @@ -26,6 +26,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/common" "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/autoinstrumentation/annotation" "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/autoinstrumentation/imageresolver" + "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/autoinstrumentation/libraryinjection" mutatecommon "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/mutate/common" configmock "github.com/DataDog/datadog-agent/pkg/config/mock" "github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels" @@ -142,6 +143,34 @@ func TestMutatePod(t *testing.T) { }, expectNoChange: true, }, + // Re-admission guard: when the webhook runs again on an already-injected pod we must not + // mutate further (e.g. must not append to LD_PRELOAD or add duplicate init containers). + "re-admission with init_container mode init container already present does not mutate": { + configPath: "testdata/filter_simple_namespace.yaml", + in: mutatecommon.FakePodSpec{ + NS: "application", + InitContainers: []corev1.Container{ + {Name: "datadog-lib-python-init", Image: "registry/dd-lib-python-init:v3"}, + }, + }.Create(), + namespaces: []workloadmeta.KubernetesMetadata{ + newTestNamespace("application", nil), + }, + expectNoChange: true, + }, + "re-admission with image_volume mode init container already present does not mutate": { + configPath: "testdata/filter_simple_namespace.yaml", + in: mutatecommon.FakePodSpec{ + NS: "application", + InitContainers: []corev1.Container{ + {Name: libraryinjection.InjectLDPreloadInitContainerName, Image: "registry/apm-inject:0"}, + }, + }.Create(), + namespaces: []workloadmeta.KubernetesMetadata{ + newTestNamespace("application", nil), + }, + expectNoChange: true, + }, "tracer configs get applied": { configPath: "testdata/filter_simple_configs.yaml", in: mutatecommon.WithLabels( diff --git a/releasenotes/notes/guard-readmission-ssi-imgvol-e9dfef7c4b5b8b0d.yaml b/releasenotes/notes/guard-readmission-ssi-imgvol-e9dfef7c4b5b8b0d.yaml new file mode 100644 index 00000000000000..97c1923281251a --- /dev/null +++ b/releasenotes/notes/guard-readmission-ssi-imgvol-e9dfef7c4b5b8b0d.yaml @@ -0,0 +1,3 @@ +fixes: + - | + Fixes a bug in the admission controller webhook that allowed admission to re-run for pods that already had APM injection in image-volume mode.