From ae8902df95d61f7c7428649651c90e38dee804df Mon Sep 17 00:00:00 2001 From: Levan Machablishvili Date: Fri, 8 May 2026 16:23:40 -0400 Subject: [PATCH 1/2] provider capabilities basic framework --- .../controller/datadogagent/feature/types.go | 11 + .../providercaps/provider_capabilities.go | 189 ++++++++++++++++++ .../controller_reconcile_agent.go | 17 +- .../controller_reconcile_v2.go | 4 +- 4 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 internal/controller/datadogagent/providercaps/provider_capabilities.go diff --git a/internal/controller/datadogagent/feature/types.go b/internal/controller/datadogagent/feature/types.go index 799a350c4d..eb3be6aab6 100644 --- a/internal/controller/datadogagent/feature/types.go +++ b/internal/controller/datadogagent/feature/types.go @@ -17,6 +17,7 @@ import ( "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1" apiutils "github.com/DataDog/datadog-operator/api/utils" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/merger" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/providercaps" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/store" ) @@ -157,6 +158,16 @@ type Feature interface { ManageOtelAgentGateway(managers PodTemplateManagers) error } +// ProviderAwareFeature is an optional interface for features that vary behaviour +// by provider. Features that have no provider-specific variation do not need +// to implement it. The reconciler applies the returned capabilities by calling +// providercaps.ApplyNodeAgentProviderCapabilities after the feature's +// ManageNodeAgent runs. +type ProviderAwareFeature interface { + Feature + NodeAgentProviderCapabilities() providercaps.NodeAgentProviderCapabilities +} + // Options option that can be pass to the Interface.Configure function type Options struct { Logger logr.Logger diff --git a/internal/controller/datadogagent/providercaps/provider_capabilities.go b/internal/controller/datadogagent/providercaps/provider_capabilities.go new file mode 100644 index 0000000000..c951a84644 --- /dev/null +++ b/internal/controller/datadogagent/providercaps/provider_capabilities.go @@ -0,0 +1,189 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package providercaps holds the provider-conditional pod-template mutation +// framework. Both per-feature (feature.ProviderAwareFeature) and global +// (global.ApplyGlobalNodeAgentSpec) consumers declare their mutations as a +// NodeAgentProviderCapabilities map and apply them via +// ApplyNodeAgentProviderCapabilities. +package providercaps + +import ( + corev1 "k8s.io/api/core/v1" + + apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/merger" +) + +// ProviderAnnotationKey is the annotation on a DDA/DDAI that declares the +// provider string. Reconcilers read this annotation to feed the provider +// through to features and to the provider-aware framework. +const ProviderAnnotationKey = "datadoghq.com/provider" + +// PodTemplateManager is the minimal interface ApplyNodeAgentProviderCapabilities +// needs from a pod-template manager. feature.PodTemplateManagers satisfies it +// structurally, so callers pass their existing manager unchanged. +type PodTemplateManager interface { + PodTemplateSpec() *corev1.PodTemplateSpec + EnvVar() merger.EnvVarManager + Volume() merger.VolumeManager + VolumeMount() merger.VolumeMountManager +} + +// VolumeAndMount groups a pod-level volume with a container volume mount. +// Volume is added to the pod spec; Mount is added to each listed container. +type VolumeAndMount struct { + Volume corev1.Volume + Mount corev1.VolumeMount + Containers []apicommon.AgentContainerName +} + +// EnvVarSet groups an env var with its target containers. +// Empty Containers means the env var is added to all agent containers. +type EnvVarSet struct { + EnvVar corev1.EnvVar + Containers []apicommon.AgentContainerName +} + +// ContainerMountRef identifies a volume mount by volume name and the containers +// it should be stripped from. Empty Containers means strip from all containers. +type ContainerMountRef struct { + VolumeName string + Containers []apicommon.AgentContainerName +} + +// ProviderCapabilities holds the volumes, env vars, and removals for a +// specific provider entry in a NodeAgentProviderCapabilities map. +type ProviderCapabilities struct { + Volumes []VolumeAndMount + EnvVars []EnvVarSet + // RemoveVolumes strips named volumes (vol + all mounts) before provider additions run. + RemoveVolumes []string + // RemoveMounts strips specific container-volume mount pairs before provider additions run. + RemoveMounts []ContainerMountRef + // RemoveEnvVars strips env vars by name before provider additions run. + RemoveEnvVars []string +} + +// NodeAgentProviderCapabilities maps a provider string to its capabilities. +// The empty string key "" is the baseline applied to all providers first. +// Provider-specific entries are then applied on top: removals first, additions second. +type NodeAgentProviderCapabilities = map[string]ProviderCapabilities + +// ApplyNodeAgentProviderCapabilities applies all provider-conditional mutations. +// The baseline ("") entry is applied first. The provider-specific entry is then +// applied: removals run before additions so a provider can replace a baseline item +// by removing it and re-adding a modified version. +func ApplyNodeAgentProviderCapabilities(mgr PodTemplateManager, provider string, caps NodeAgentProviderCapabilities) { + if len(caps) == 0 { + return + } + + applyAdditions := func(c ProviderCapabilities) { + addedVolumes := make(map[string]bool) + for _, vm := range c.Volumes { + if !addedVolumes[vm.Volume.Name] { + mgr.Volume().AddVolume(&vm.Volume) + addedVolumes[vm.Volume.Name] = true + } + mgr.VolumeMount().AddVolumeMountToContainers(&vm.Mount, vm.Containers) + } + for _, ev := range c.EnvVars { + if len(ev.Containers) == 0 { + mgr.EnvVar().AddEnvVar(&ev.EnvVar) + } else { + mgr.EnvVar().AddEnvVarToContainers(ev.Containers, &ev.EnvVar) + } + } + } + + applyRemovals := func(c ProviderCapabilities) { + tmpl := mgr.PodTemplateSpec() + for _, name := range c.RemoveVolumes { + stripVolume(tmpl, name) + } + for _, ref := range c.RemoveMounts { + stripMounts(tmpl, ref.VolumeName, ref.Containers) + } + for _, name := range c.RemoveEnvVars { + stripEnvVar(tmpl, name) + } + } + + if baseline, ok := caps[""]; ok { + applyAdditions(baseline) + } + if provider != "" { + if providerCaps, ok := caps[provider]; ok { + applyRemovals(providerCaps) + applyAdditions(providerCaps) + } + } +} + +// stripVolume removes a named volume from the pod spec and all its mounts +// from every container and init container. +func stripVolume(tmpl *corev1.PodTemplateSpec, volumeName string) { + filtered := tmpl.Spec.Volumes[:0] + for _, v := range tmpl.Spec.Volumes { + if v.Name != volumeName { + filtered = append(filtered, v) + } + } + tmpl.Spec.Volumes = filtered + stripMounts(tmpl, volumeName, nil) +} + +// stripMounts removes the mount for volumeName from the specified containers. +// If containers is nil or empty, the mount is stripped from every container +// and init container. +func stripMounts(tmpl *corev1.PodTemplateSpec, volumeName string, containers []apicommon.AgentContainerName) { + targetAll := len(containers) == 0 + targetSet := make(map[string]bool, len(containers)) + for _, c := range containers { + targetSet[string(c)] = true + } + + for i := range tmpl.Spec.Containers { + if targetAll || targetSet[tmpl.Spec.Containers[i].Name] { + tmpl.Spec.Containers[i].VolumeMounts = removeMountByName(tmpl.Spec.Containers[i].VolumeMounts, volumeName) + } + } + for i := range tmpl.Spec.InitContainers { + if targetAll || targetSet[tmpl.Spec.InitContainers[i].Name] { + tmpl.Spec.InitContainers[i].VolumeMounts = removeMountByName(tmpl.Spec.InitContainers[i].VolumeMounts, volumeName) + } + } +} + +// stripEnvVar removes an env var by name from every container and init container. +func stripEnvVar(tmpl *corev1.PodTemplateSpec, name string) { + for i := range tmpl.Spec.Containers { + tmpl.Spec.Containers[i].Env = removeEnvVarByName(tmpl.Spec.Containers[i].Env, name) + } + for i := range tmpl.Spec.InitContainers { + tmpl.Spec.InitContainers[i].Env = removeEnvVarByName(tmpl.Spec.InitContainers[i].Env, name) + } +} + +func removeMountByName(mounts []corev1.VolumeMount, name string) []corev1.VolumeMount { + out := mounts[:0] + for _, m := range mounts { + if m.Name != name { + out = append(out, m) + } + } + return out +} + +func removeEnvVarByName(envs []corev1.EnvVar, name string) []corev1.EnvVar { + out := envs[:0] + for _, e := range envs { + if e.Name != name { + out = append(out, e) + } + } + return out +} diff --git a/internal/controller/datadogagentinternal/controller_reconcile_agent.go b/internal/controller/datadogagentinternal/controller_reconcile_agent.go index a16ce34276..6403822f9e 100644 --- a/internal/controller/datadogagentinternal/controller_reconcile_agent.go +++ b/internal/controller/datadogagentinternal/controller_reconcile_agent.go @@ -26,6 +26,7 @@ import ( "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/global" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/override" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/providercaps" "github.com/DataDog/datadog-operator/pkg/condition" "github.com/DataDog/datadog-operator/pkg/constants" "github.com/DataDog/datadog-operator/pkg/controller/utils/datadog" @@ -33,7 +34,7 @@ import ( ) func (r *Reconciler) reconcileV2Agent(ctx context.Context, requiredComponents feature.RequiredComponents, features []feature.Feature, - ddai *datadoghqv1alpha1.DatadogAgentInternal, resourcesManager feature.ResourceManagers, newStatus *datadoghqv1alpha1.DatadogAgentInternalStatus) (reconcile.Result, error) { + ddai *datadoghqv1alpha1.DatadogAgentInternal, resourcesManager feature.ResourceManagers, newStatus *datadoghqv1alpha1.DatadogAgentInternalStatus, provider string) (reconcile.Result, error) { var result reconcile.Result var eds *edsv1alpha1.ExtendedDaemonSet var daemonset *appsv1.DaemonSet @@ -63,11 +64,16 @@ func (r *Reconciler) reconcileV2Agent(ctx context.Context, requiredComponents fe // Set Global setting on the default extendeddaemonset global.ApplyGlobalSettingsNodeAgent(objLogger, podManagers, ddai.GetObjectMeta(), &ddai.Spec, resourcesManager, singleContainerStrategyEnabled, requiredComponents) - // Apply features changes on the Deployment.Spec.Template + // Apply features changes on the Deployment.Spec.Template. + // Provider capabilities are applied immediately after each feature's ManageNodeAgent + // so that each feature owns its provider correctness independently. for _, feat := range features { if errFeat := feat.ManageNodeAgent(podManagers); errFeat != nil { return result, errFeat } + if paf, ok := feat.(feature.ProviderAwareFeature); ok { + providercaps.ApplyNodeAgentProviderCapabilities(podManagers, provider, paf.NodeAgentProviderCapabilities()) + } } // If Override is defined for the node agent component, apply the override on the PodTemplateSpec, it will cascade to container. @@ -115,7 +121,9 @@ func (r *Reconciler) reconcileV2Agent(ctx context.Context, requiredComponents fe // Set Global setting on the default daemonset global.ApplyGlobalSettingsNodeAgent(objLogger, podManagers, ddai.GetObjectMeta(), &ddai.Spec, resourcesManager, singleContainerStrategyEnabled, requiredComponents) - // Apply features changes on the Deployment.Spec.Template + // Apply features changes on the Deployment.Spec.Template. + // Provider capabilities are applied immediately after each feature's ManageNodeAgent + // so that each feature owns its provider correctness independently. for _, feat := range features { if singleContainerStrategyEnabled { if errFeat := feat.ManageSingleContainerNodeAgent(podManagers); errFeat != nil { @@ -125,6 +133,9 @@ func (r *Reconciler) reconcileV2Agent(ctx context.Context, requiredComponents fe if errFeat := feat.ManageNodeAgent(podManagers); errFeat != nil { return result, errFeat } + if paf, ok := feat.(feature.ProviderAwareFeature); ok { + providercaps.ApplyNodeAgentProviderCapabilities(podManagers, provider, paf.NodeAgentProviderCapabilities()) + } } } diff --git a/internal/controller/datadogagentinternal/controller_reconcile_v2.go b/internal/controller/datadogagentinternal/controller_reconcile_v2.go index fa7d8c0afb..4d59f1f156 100644 --- a/internal/controller/datadogagentinternal/controller_reconcile_v2.go +++ b/internal/controller/datadogagentinternal/controller_reconcile_v2.go @@ -18,6 +18,7 @@ import ( "github.com/DataDog/datadog-operator/internal/controller/datadogagent/common" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/defaults" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/providercaps" "github.com/DataDog/datadog-operator/internal/controller/finalizer" "github.com/DataDog/datadog-operator/pkg/condition" "github.com/DataDog/datadog-operator/pkg/constants" @@ -100,7 +101,8 @@ func (r *Reconciler) reconcileInstanceV2(ctx context.Context, instance *v1alpha1 } // 2.b. Node Agent - result, err = r.reconcileV2Agent(ctx, requiredComponents, append(configuredFeatures, enabledFeatures...), instance, resourceManagers, newStatus) + provider := instance.GetAnnotations()[providercaps.ProviderAnnotationKey] + result, err = r.reconcileV2Agent(ctx, requiredComponents, append(configuredFeatures, enabledFeatures...), instance, resourceManagers, newStatus, provider) if utils.ShouldReturn(result, err) { return r.updateStatusIfNeededV2(ctx, instance, newStatus, result, err, now) } From 97d838ec57aac9e419409e7de168b350ca0f6752 Mon Sep 17 00:00:00 2001 From: Levan Machablishvili Date: Fri, 8 May 2026 16:41:28 -0400 Subject: [PATCH 2/2] Support providers.eks.ec2.useHostnameFromFile --- .../datadogagent/global/provider_spec.go | 84 ++++++++++++++++ .../datadogagent/global/provider_spec_test.go | 95 +++++++++++++++++++ .../providercaps/provider_capabilities.go | 15 +-- .../controller_reconcile_agent.go | 7 ++ .../controller_reconcile_v2.go | 4 +- pkg/kubernetes/provider.go | 5 + 6 files changed, 201 insertions(+), 9 deletions(-) create mode 100644 internal/controller/datadogagent/global/provider_spec.go create mode 100644 internal/controller/datadogagent/global/provider_spec_test.go diff --git a/internal/controller/datadogagent/global/provider_spec.go b/internal/controller/datadogagent/global/provider_spec.go new file mode 100644 index 0000000000..0e9a8ef72e --- /dev/null +++ b/internal/controller/datadogagent/global/provider_spec.go @@ -0,0 +1,84 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// This file is the declarative manifest of provider-conditional global +// mutations applied to the node agent pod template. It contains constants, +// type definitions, and the spec map. Behaviour (applier, helpers) lives in +// provider_apply.go. + +package global + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + + apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/providercaps" + "github.com/DataDog/datadog-operator/pkg/kubernetes" +) + +// cloudInitInstanceIDPath is the host path EKS-EC2 nodes expose containing the +// EC2 instance-id, used by the agent to derive a stable hostname. +const cloudInitInstanceIDPath = "/var/lib/cloud/data/instance-id" + +// cloudInitInstanceIDVolumeName is the pod-level volume name for the cloud-init +// instance-id file. +const cloudInitInstanceIDVolumeName = "cloudinit-instance-id-file" + +// NodeAgentProviderSpec is the provider-keyed capabilities map for the node +// agent pod template. The "" baseline applies to all providers; provider-keyed +// entries are applied on top (removals first, then additions). +// +// Mirrors the Helm chart's _provider-specific_ pod-template additions (see +// charts/datadog/templates/_containers-common-env.yaml and +// _container-cloudinit-volumemounts.yaml). +var NodeAgentProviderSpec = providercaps.NodeAgentProviderCapabilities{ + kubernetes.EKSEC2UseHostnameFromFileProvider: { + // DD_HOSTNAME_FILE points the agent at the EC2 instance-id so it + // derives a stable hostname even when the kubelet hostname differs. + // Helm includes this in containers-common-env, which renders into + // every main container AND every init container; mirror that. + EnvVars: []providercaps.EnvVarSet{ + { + EnvVar: corev1.EnvVar{ + Name: "DD_HOSTNAME_FILE", + Value: cloudInitInstanceIDPath, + }, + InitContainers: []apicommon.AgentContainerName{ + apicommon.InitConfigContainerName, + }, + }, + }, + // HostPath mount of the instance-id file. Helm adds this on every + // agent-side main container; enumerate the same set. + Volumes: []providercaps.VolumeAndMount{ + { + Volume: corev1.Volume{ + Name: cloudInitInstanceIDVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: cloudInitInstanceIDPath, + Type: ptr.To(corev1.HostPathFile), + }, + }, + }, + Mount: corev1.VolumeMount{ + Name: cloudInitInstanceIDVolumeName, + MountPath: cloudInitInstanceIDPath, + ReadOnly: true, + }, + Containers: []apicommon.AgentContainerName{ + apicommon.CoreAgentContainerName, + apicommon.TraceAgentContainerName, + apicommon.ProcessAgentContainerName, + apicommon.SystemProbeContainerName, + apicommon.SecurityAgentContainerName, + apicommon.AgentDataPlaneContainerName, + apicommon.OtelAgent, + }, + }, + }, + }, +} diff --git a/internal/controller/datadogagent/global/provider_spec_test.go b/internal/controller/datadogagent/global/provider_spec_test.go new file mode 100644 index 0000000000..003c920b1b --- /dev/null +++ b/internal/controller/datadogagent/global/provider_spec_test.go @@ -0,0 +1,95 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package global + +import ( + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" + + apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature" + "github.com/DataDog/datadog-operator/internal/controller/datadogagent/providercaps" + "github.com/DataDog/datadog-operator/pkg/kubernetes" +) + +// TestNodeAgentProviderSpec_EKS asserts the EKS-EC2 provider spec injects: +// - DD_HOSTNAME_FILE env var on every main container and on init-config +// - cloudinit-instance-id-file HostPath volume + read-only mount on every +// enumerated agent container +func TestNodeAgentProviderSpec_EKS(t *testing.T) { + tmpl := &corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: string(apicommon.CoreAgentContainerName)}, + {Name: string(apicommon.TraceAgentContainerName)}, + }, + InitContainers: []corev1.Container{ + {Name: string(apicommon.InitVolumeContainerName)}, + {Name: string(apicommon.InitConfigContainerName)}, + }, + }, + } + mgr := feature.NewPodTemplateManagers(tmpl) + + providercaps.ApplyNodeAgentProviderCapabilities(mgr, kubernetes.EKSEC2UseHostnameFromFileProvider, NodeAgentProviderSpec) + + wantEnv := corev1.EnvVar{Name: "DD_HOSTNAME_FILE", Value: "/var/lib/cloud/data/instance-id"} + for _, c := range tmpl.Spec.Containers { + assert.Contains(t, c.Env, wantEnv, "main container %q should get DD_HOSTNAME_FILE", c.Name) + } + // init-config receives the env var; init-volume does not (matches helm). + var initConfig, initVolume corev1.Container + for _, c := range tmpl.Spec.InitContainers { + switch c.Name { + case string(apicommon.InitConfigContainerName): + initConfig = c + case string(apicommon.InitVolumeContainerName): + initVolume = c + } + } + assert.Contains(t, initConfig.Env, wantEnv, "init-config should get DD_HOSTNAME_FILE") + assert.NotContains(t, initVolume.Env, wantEnv, "init-volume should NOT get DD_HOSTNAME_FILE") + + // HostPath volume is added at pod level. + var vol corev1.Volume + for _, v := range tmpl.Spec.Volumes { + if v.Name == "cloudinit-instance-id-file" { + vol = v + } + } + assert.NotNil(t, vol.HostPath, "cloudinit-instance-id-file volume should be a HostPath") + assert.Equal(t, "/var/lib/cloud/data/instance-id", vol.HostPath.Path) + assert.Equal(t, ptr.To(corev1.HostPathFile), vol.HostPath.Type) + + // Read-only mount is added on each main container. + wantMount := corev1.VolumeMount{ + Name: "cloudinit-instance-id-file", + MountPath: "/var/lib/cloud/data/instance-id", + ReadOnly: true, + } + for _, c := range tmpl.Spec.Containers { + assert.Contains(t, c.VolumeMounts, wantMount, "main container %q should mount cloudinit-instance-id-file", c.Name) + } +} + +// TestNodeAgentProviderSpec_NoProvider asserts no provider == no mutations. +func TestNodeAgentProviderSpec_NoProvider(t *testing.T) { + tmpl := &corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{Name: string(apicommon.CoreAgentContainerName)}}, + }, + } + mgr := feature.NewPodTemplateManagers(tmpl) + + providercaps.ApplyNodeAgentProviderCapabilities(mgr, "", NodeAgentProviderSpec) + + assert.Empty(t, tmpl.Spec.Containers[0].Env) + assert.Empty(t, tmpl.Spec.Containers[0].VolumeMounts) + assert.Empty(t, tmpl.Spec.Volumes) +} diff --git a/internal/controller/datadogagent/providercaps/provider_capabilities.go b/internal/controller/datadogagent/providercaps/provider_capabilities.go index c951a84644..daad0b8ca3 100644 --- a/internal/controller/datadogagent/providercaps/provider_capabilities.go +++ b/internal/controller/datadogagent/providercaps/provider_capabilities.go @@ -17,11 +17,6 @@ import ( "github.com/DataDog/datadog-operator/internal/controller/datadogagent/merger" ) -// ProviderAnnotationKey is the annotation on a DDA/DDAI that declares the -// provider string. Reconcilers read this annotation to feed the provider -// through to features and to the provider-aware framework. -const ProviderAnnotationKey = "datadoghq.com/provider" - // PodTemplateManager is the minimal interface ApplyNodeAgentProviderCapabilities // needs from a pod-template manager. feature.PodTemplateManagers satisfies it // structurally, so callers pass their existing manager unchanged. @@ -42,9 +37,12 @@ type VolumeAndMount struct { // EnvVarSet groups an env var with its target containers. // Empty Containers means the env var is added to all agent containers. +// InitContainers lists init containers that should also receive the env var +// (init containers are not iterated by the all-containers AddEnvVar path). type EnvVarSet struct { - EnvVar corev1.EnvVar - Containers []apicommon.AgentContainerName + EnvVar corev1.EnvVar + Containers []apicommon.AgentContainerName + InitContainers []apicommon.AgentContainerName } // ContainerMountRef identifies a volume mount by volume name and the containers @@ -96,6 +94,9 @@ func ApplyNodeAgentProviderCapabilities(mgr PodTemplateManager, provider string, } else { mgr.EnvVar().AddEnvVarToContainers(ev.Containers, &ev.EnvVar) } + for _, ic := range ev.InitContainers { + mgr.EnvVar().AddEnvVarToInitContainer(ic, &ev.EnvVar) + } } } diff --git a/internal/controller/datadogagentinternal/controller_reconcile_agent.go b/internal/controller/datadogagentinternal/controller_reconcile_agent.go index 6403822f9e..215f3998f5 100644 --- a/internal/controller/datadogagentinternal/controller_reconcile_agent.go +++ b/internal/controller/datadogagentinternal/controller_reconcile_agent.go @@ -61,6 +61,9 @@ func (r *Reconciler) reconcileV2Agent(ctx context.Context, requiredComponents fe objLogger := daemonsetLogger.WithValues("object.kind", "ExtendedDaemonSet", "object.namespace", eds.Namespace, "object.name", eds.Name) podManagers = feature.NewPodTemplateManagers(&eds.Spec.Template) + // Apply provider-conditional global mutations to the pod template — pre-feature. + providercaps.ApplyNodeAgentProviderCapabilities(podManagers, provider, global.NodeAgentProviderSpec) + // Set Global setting on the default extendeddaemonset global.ApplyGlobalSettingsNodeAgent(objLogger, podManagers, ddai.GetObjectMeta(), &ddai.Spec, resourcesManager, singleContainerStrategyEnabled, requiredComponents) @@ -118,6 +121,10 @@ func (r *Reconciler) reconcileV2Agent(ctx context.Context, requiredComponents fe daemonset = componentagent.NewDefaultAgentDaemonset(ddai, &r.options.ExtendedDaemonsetOptions, requiredComponents.Agent, component.GetAgentName(ddai)) objLogger := daemonsetLogger.WithValues("object.kind", "DaemonSet", "object.namespace", daemonset.Namespace, "object.name", daemonset.Name) podManagers = feature.NewPodTemplateManagers(&daemonset.Spec.Template) + + // Apply provider-conditional global mutations to the pod template — pre-feature. + providercaps.ApplyNodeAgentProviderCapabilities(podManagers, provider, global.NodeAgentProviderSpec) + // Set Global setting on the default daemonset global.ApplyGlobalSettingsNodeAgent(objLogger, podManagers, ddai.GetObjectMeta(), &ddai.Spec, resourcesManager, singleContainerStrategyEnabled, requiredComponents) diff --git a/internal/controller/datadogagentinternal/controller_reconcile_v2.go b/internal/controller/datadogagentinternal/controller_reconcile_v2.go index 4d59f1f156..1dae2fdb00 100644 --- a/internal/controller/datadogagentinternal/controller_reconcile_v2.go +++ b/internal/controller/datadogagentinternal/controller_reconcile_v2.go @@ -18,11 +18,11 @@ import ( "github.com/DataDog/datadog-operator/internal/controller/datadogagent/common" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/defaults" "github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature" - "github.com/DataDog/datadog-operator/internal/controller/datadogagent/providercaps" "github.com/DataDog/datadog-operator/internal/controller/finalizer" "github.com/DataDog/datadog-operator/pkg/condition" "github.com/DataDog/datadog-operator/pkg/constants" "github.com/DataDog/datadog-operator/pkg/controller/utils" + "github.com/DataDog/datadog-operator/pkg/kubernetes" ) func (r *Reconciler) internalReconcileV2(ctx context.Context, instance *v1alpha1.DatadogAgentInternal) (reconcile.Result, error) { @@ -101,7 +101,7 @@ func (r *Reconciler) reconcileInstanceV2(ctx context.Context, instance *v1alpha1 } // 2.b. Node Agent - provider := instance.GetAnnotations()[providercaps.ProviderAnnotationKey] + provider := instance.GetAnnotations()[kubernetes.ProviderAnnotationKey] result, err = r.reconcileV2Agent(ctx, requiredComponents, append(configuredFeatures, enabledFeatures...), instance, resourceManagers, newStatus, provider) if utils.ShouldReturn(result, err) { return r.updateStatusIfNeededV2(ctx, instance, newStatus, result, err, now) diff --git a/pkg/kubernetes/provider.go b/pkg/kubernetes/provider.go index 746bb9cba7..daad33cf28 100644 --- a/pkg/kubernetes/provider.go +++ b/pkg/kubernetes/provider.go @@ -44,6 +44,11 @@ const ( // EKSCloudProvider is the Amazon EKS CloudProvider name EKSCloudProvider = "eks" + // EKSEC2UseHostnameFromFileProvider is the EKS-EC2 provider variant where + // the agent reads its hostname from the cloud-init instance-id file mounted + // from the host (mirrors helm's providers.eks.ec2.useHostnameFromFile). + EKSEC2UseHostnameFromFileProvider = "eks-ec2-use-hostname-from-file" + // EKSProviderLabel is a common EKS node label containing the AMI ID EKSProviderLabel = "eks.amazonaws.com/nodegroup-image"