diff --git a/api/datadoghq/v2alpha1/datadogagent_types.go b/api/datadoghq/v2alpha1/datadogagent_types.go index fcf2b3c2d..b1e858af5 100644 --- a/api/datadoghq/v2alpha1/datadogagent_types.go +++ b/api/datadoghq/v2alpha1/datadogagent_types.go @@ -1011,6 +1011,12 @@ type GlobalConfig struct { // +listType=set Tags []string `json:"tags,omitempty"` + //Env contains a list of environment variables that are set for all Agents. + // +optional + // +listType=map + // +listMapKey=name + Env []corev1.EnvVar `json:"env,omitempty"` + // OriginDetectionUnified defines the origin detection unified mechanism behavior. // +optional OriginDetectionUnified *OriginDetectionUnified `json:"originDetectionUnified,omitempty"` diff --git a/api/datadoghq/v2alpha1/test/builder.go b/api/datadoghq/v2alpha1/test/builder.go index 8fc819d02..e9b203a89 100644 --- a/api/datadoghq/v2alpha1/test/builder.go +++ b/api/datadoghq/v2alpha1/test/builder.go @@ -72,6 +72,12 @@ func (builder *DatadogAgentBuilder) WithName(name string) *DatadogAgentBuilder { return builder } +// Global environment variable +func (builder *DatadogAgentBuilder) WithEnvVars(envs []corev1.EnvVar) *DatadogAgentBuilder { + builder.datadogAgent.Spec.Global.Env = envs + return builder +} + // Dogstatsd func (builder *DatadogAgentBuilder) initDogstatsd() { if builder.datadogAgent.Spec.Features.Dogstatsd == nil { diff --git a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go index 11322f795..185a5aef0 100644 --- a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go +++ b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go @@ -1380,6 +1380,13 @@ func (in *GlobalConfig) DeepCopyInto(out *GlobalConfig) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.OriginDetectionUnified != nil { in, out := &in.OriginDetectionUnified, &out.OriginDetectionUnified *out = new(OriginDetectionUnified) diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml index 596447a82..5ead3f217 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml @@ -1536,6 +1536,112 @@ spec: description: URL defines the endpoint URL. type: string type: object + env: + description: Env contains a list of environment variables that are set for all Agents. + items: + description: EnvVar represents an environment variable present in a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map fips: description: FIPS contains configuration used to customize the FIPS proxy sidecar. properties: diff --git a/docs/configuration.v2alpha1.md b/docs/configuration.v2alpha1.md index 81f97a5be..bdb28d7cf 100644 --- a/docs/configuration.v2alpha1.md +++ b/docs/configuration.v2alpha1.md @@ -179,6 +179,7 @@ spec: | global.endpoint.credentials.appSecret.keyName | KeyName is the key of the secret to use. | | global.endpoint.credentials.appSecret.secretName | SecretName is the name of the secret. | | global.endpoint.url | URL defines the endpoint URL. | +| global.env | Env contains a list of environment variables that are set for all Agents. | | global.fips.customFIPSConfig.configData | ConfigData corresponds to the configuration file content. | | global.fips.customFIPSConfig.configMap.items | Items maps a ConfigMap data `key` to a file `path` mount. | | global.fips.customFIPSConfig.configMap.name | Name is the name of the ConfigMap. | diff --git a/internal/controller/datadogagent/override/global.go b/internal/controller/datadogagent/override/global.go index b99f74ce1..401648d00 100644 --- a/internal/controller/datadogagent/override/global.go +++ b/internal/controller/datadogagent/override/global.go @@ -137,6 +137,13 @@ func applyGlobalSettings(logger logr.Logger, manager feature.PodTemplateManagers } } + // Env is a list of custom global variables that are set across all agents. + if config.Env != nil { + for _, envVar := range config.Env { + manager.EnvVar().AddEnvVar(&envVar) + } + } + if config.OriginDetectionUnified != nil && config.OriginDetectionUnified.Enabled != nil { manager.EnvVar().AddEnvVar(&corev1.EnvVar{ Name: apicommon.DDOriginDetectionUnified, diff --git a/internal/controller/datadogagent/override/global_test.go b/internal/controller/datadogagent/override/global_test.go index 9f5cec553..a1182c06f 100644 --- a/internal/controller/datadogagent/override/global_test.go +++ b/internal/controller/datadogagent/override/global_test.go @@ -116,6 +116,35 @@ func TestNodeAgentComponenGlobalSettings(t *testing.T) { wantVolumes: emptyVolumes, want: assertAll, }, + { + name: "Global environment variable configured", + singleContainerStrategyEnabled: false, + dda: v2alpha1test.NewDatadogAgentBuilder(). + WithEnvVars([]corev1.EnvVar{ + { + Name: "envA", + Value: "valueA", + }, + { + Name: "envB", + Value: "valueB", + }, + }). + BuildWithDefaults(), + wantEnvVars: getExpectedEnvVars([]*corev1.EnvVar{ + { + Name: "envA", + Value: "valueA", + }, + { + Name: "envB", + Value: "valueB", + }, + }...), + wantVolumeMounts: emptyVolumeMounts, + wantVolumes: emptyVolumes, + want: assertAll, + }, } for _, tt := range tests { diff --git a/internal/controller/testutils/agent.go b/internal/controller/testutils/agent.go index ffc57a4d3..379aea705 100644 --- a/internal/controller/testutils/agent.go +++ b/internal/controller/testutils/agent.go @@ -361,9 +361,19 @@ func NewDatadogAgentWithGlobalConfigSettings(namespace string, name string) v2al AppKey: apiutils.NewStringPointer("my-app-key"), }, }, - Registry: apiutils.NewStringPointer("my-custom-registry"), - LogLevel: apiutils.NewStringPointer("INFO"), - Tags: []string{"tagA:valA", "tagB:valB"}, + Registry: apiutils.NewStringPointer("my-custom-registry"), + LogLevel: apiutils.NewStringPointer("INFO"), + Tags: []string{"tagA:valA", "tagB:valB"}, + Env: []v1.EnvVar{ + { + Name: "some-envA", + Value: "some-valA", + }, + { + Name: "some-envB", + Value: "some-valB", + }, + }, PodLabelsAsTags: map[string]string{"some-label": "some-tag"}, PodAnnotationsAsTags: map[string]string{"some-annotation": "some-tag"}, NodeLabelsAsTags: map[string]string{"some-label": "some-tag"},