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.