Skip to content

Commit

Permalink
Load environment variables in override with envFrom (#1429)
Browse files Browse the repository at this point in the history
* load variables in override w/ envFrom

* fix broken reference to commonv1

* fix bug with mocking containers

* add test cases for envfrom override

* change assert to ElementsMatch

* remove unused container objects

* remove comment

---------

Co-authored-by: Fanny Jiang <[email protected]>
  • Loading branch information
swang392 and fanny-jiang authored Oct 2, 2024
1 parent fdaeb06 commit 5e5ce7e
Show file tree
Hide file tree
Showing 11 changed files with 324 additions and 71 deletions.
5 changes: 5 additions & 0 deletions api/datadoghq/v2alpha1/datadogagent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1384,6 +1384,11 @@ type DatadogAgentComponentOverride struct {
// +listMapKey=name
Env []corev1.EnvVar `json:"env,omitempty"`

// EnvFrom specifies the ConfigMaps and Secrets to expose as environment variables.
// Priority is env > envFrom.
// +optional
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`

// CustomConfiguration allows to specify custom configuration files for `datadog.yaml`, `datadog-cluster.yaml`, `security-agent.yaml`, and `system-probe.yaml`.
// The content is merged with configuration generated by the Datadog Operator, with priority given to custom configuration.
// WARNING: It is possible to override values set in the `DatadogAgent`.
Expand Down
7 changes: 7 additions & 0 deletions api/datadoghq/v2alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions config/crd/bases/v1/datadoghq.com_datadogagents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3787,6 +3787,46 @@ spec:
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
envFrom:
description: |-
EnvFrom specifies the ConfigMaps and Secrets to expose as environment variables.
Priority is env > envFrom.
items:
description: EnvFromSource represents the source of a set of ConfigMaps
properties:
configMapRef:
description: The ConfigMap to select from
properties:
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 must be defined
type: boolean
type: object
x-kubernetes-map-type: atomic
prefix:
description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.
type: string
secretRef:
description: The Secret to select from
properties:
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 must be defined
type: boolean
type: object
x-kubernetes-map-type: atomic
type: object
type: array
extraChecksd:
description: |-
Checksd configuration allowing to specify custom checks placed under /etc/datadog-agent/checks.d/
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.v2alpha1.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ In the table, `spec.override.nodeAgent.image.name` and `spec.override.nodeAgent.
| [key].dnsConfig.searches | A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed. |
| [key].dnsPolicy | Set DNS policy for the pod. Defaults to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. |
| [key].env `[]object` | Specify additional environment variables for all containers in this component Priority is Container > Component. See also: https://docs.datadoghq.com/agent/kubernetes/?tab=helm#environment-variables |
| [key].envFrom `[]object` | EnvFrom specifies the ConfigMaps and Secrets to expose as environment variables. Priority is env > envFrom. |
| [key].extraChecksd.configDataMap | ConfigDataMap corresponds to the content of the configuration files. The key should be the filename the contents get mounted to; for instance check.py or check.yaml. |
| [key].extraChecksd.configMap.items | Items maps a ConfigMap data `key` to a file `path` mount. |
| [key].extraChecksd.configMap.name | Name is the name of the ConfigMap. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type PodTemplateManagers struct {
Tpl v1.PodTemplateSpec
EnvVarMgr *mergerfake.EnvVarManager
EnvFromVarMgr *mergerfake.EnvFromVarManager
VolumeMgr *mergerfake.VolumeManager
VolumeMountMgr *mergerfake.VolumeMountManager
SecurityContextMgr *mergerfake.SecurityContextManager
Expand All @@ -25,6 +26,11 @@ func (_m *PodTemplateManagers) EnvVar() merger.EnvVarManager {
return _m.EnvVarMgr
}

// EnvFromVar provides a mock function with given fields:
func (_m *PodTemplateManagers) EnvFromVar() merger.EnvFromVarManager {
return _m.EnvFromVarMgr
}

// PodTemplateSpec provides a mock function with given fields:
func (_m *PodTemplateManagers) PodTemplateSpec() *v1.PodTemplateSpec {
return &_m.Tpl
Expand Down Expand Up @@ -60,6 +66,7 @@ func NewPodTemplateManagers(t testing.TB, podTpl v1.PodTemplateSpec) *PodTemplat
return &PodTemplateManagers{
Tpl: podTpl,
EnvVarMgr: mergerfake.NewFakeEnvVarManager(t),
EnvFromVarMgr: mergerfake.NewFakeEnvFromVarManager(t),
VolumeMgr: mergerfake.NewFakeVolumeManager(t),
VolumeMountMgr: mergerfake.NewFakeVolumeMountManager(t),
SecurityContextMgr: mergerfake.NewFakeSecurityContextManager(t),
Expand Down
8 changes: 8 additions & 0 deletions internal/controller/datadogagent/feature/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ type PodTemplateManagers interface {
PodTemplateSpec() *corev1.PodTemplateSpec
// EnvVar used to access the EnvVarManager to manage the Environment variable defined in the PodTemplateSpec.
EnvVar() merger.EnvVarManager
// EnvVarFrom used to access the EnvVarFromManager to manage the Environment variable defined in the PodTemplateSpec.
EnvFromVar() merger.EnvFromVarManager
// Volume used to access the VolumeManager to manage the Volume defined in the PodTemplateSpec.
Volume() merger.VolumeManager
// VolumeMount used to access the VolumeMountManager to manage the VolumeMount defined in the PodTemplateSpec.
Expand All @@ -259,6 +261,7 @@ func NewPodTemplateManagers(podTmpl *corev1.PodTemplateSpec) PodTemplateManagers
return &podTemplateManagerImpl{
podTmpl: podTmpl,
envVarManager: merger.NewEnvVarManager(podTmpl),
envFromVarManager: merger.NewEnvFromVarManager(podTmpl),
volumeManager: merger.NewVolumeManager(podTmpl),
volumeMountManager: merger.NewVolumeMountManager(podTmpl),
securityContextManager: merger.NewSecurityContextManager(podTmpl),
Expand All @@ -270,6 +273,7 @@ func NewPodTemplateManagers(podTmpl *corev1.PodTemplateSpec) PodTemplateManagers
type podTemplateManagerImpl struct {
podTmpl *corev1.PodTemplateSpec
envVarManager merger.EnvVarManager
envFromVarManager merger.EnvFromVarManager
volumeManager merger.VolumeManager
volumeMountManager merger.VolumeMountManager
securityContextManager merger.SecurityContextManager
Expand All @@ -285,6 +289,10 @@ func (impl *podTemplateManagerImpl) EnvVar() merger.EnvVarManager {
return impl.envVarManager
}

func (impl *podTemplateManagerImpl) EnvFromVar() merger.EnvFromVarManager {
return impl.envFromVarManager
}

func (impl *podTemplateManagerImpl) Volume() merger.VolumeManager {
return impl.volumeManager
}
Expand Down
94 changes: 84 additions & 10 deletions internal/controller/datadogagent/merger/envvarfrom.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,93 @@
package merger

import (
"github.com/DataDog/datadog-operator/api/datadoghq/common"
corev1 "k8s.io/api/core/v1"
)

// EnvFromVarManager use to manage adding Environment variable to container in a PodTemplateSpec
type EnvFromVarManager interface {
// AddEnvFromVar use to add an envFromSource variable to all containers present in the Pod.
AddEnvFromVar(newEnvVar *corev1.EnvFromSource)
// AddEnvFromVarWithMergeFunc is used to add an envFromSource variable to all containers present in the Pod.
// The way the EnvFromVar is merged with an existing EnvFromVar can be tuned thanks to the EnvFromSourceFromMergeFunction parameter.
AddEnvFromVarWithMergeFunc(newEnvFromVar *corev1.EnvFromSource, mergeFunc EnvFromSourceFromMergeFunction) error
// AddEnvFromVarToContainer is used to add an envFromSource to a specific container present in the Pod.
AddEnvFromVarToContainer(containerName common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource)
// AddEnvFromVarToContainers is used to add an envFromSource variable to specified containers present in the Pod.
AddEnvFromVarToContainers(containerNames []common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource)
// AddEnvFromVarToInitContainer is used to add an envFromSource variable to a specific init container present in the Pod.
AddEnvFromVarToInitContainer(containerName common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource)
// AddEnvFromVarToContainerWithMergeFunc use to add an envFromSource variable to a specific container present in the Pod.
// The way the EnvFromVar is merged with an existing EnvFromVar can be tuned thanks to the EnvFromSourceFromMergeFunction parameter.
AddEnvFromVarToContainerWithMergeFunc(containerName common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource, mergeFunc EnvFromSourceFromMergeFunction) error
}

// NewEnvFromManager returns new instance of the EnvFromVarManager
func NewEnvFromVarManager(podTmpl *corev1.PodTemplateSpec) EnvFromVarManager {
return &envFromVarManagerImpl{
podTmpl: podTmpl,
}
}

type envFromVarManagerImpl struct {
podTmpl *corev1.PodTemplateSpec
}

func (impl *envFromVarManagerImpl) AddEnvFromVar(newEnvFromVar *corev1.EnvFromSource) {
_ = impl.AddEnvFromVarWithMergeFunc(newEnvFromVar, DefaultEnvFromSourceFromMergeFunction)
}

func (impl *envFromVarManagerImpl) AddEnvFromVarWithMergeFunc(newEnvFromVar *corev1.EnvFromSource, mergeFunc EnvFromSourceFromMergeFunction) error {
for id, cont := range impl.podTmpl.Spec.Containers {
if _, ok := AllAgentContainers[common.AgentContainerName(cont.Name)]; ok {
_, err := AddEnvFromSourceFromToContainer(&impl.podTmpl.Spec.Containers[id], newEnvFromVar, mergeFunc)
if err != nil {
return err
}
}
}
return nil
}

func (impl *envFromVarManagerImpl) AddEnvFromVarToContainer(containerName common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource) {
_ = impl.AddEnvFromVarToContainerWithMergeFunc(containerName, newEnvFromVar, DefaultEnvFromSourceFromMergeFunction)
}

func (impl *envFromVarManagerImpl) AddEnvFromVarToContainers(containerNames []common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource) {
for _, containerName := range containerNames {
_ = impl.AddEnvFromVarToContainerWithMergeFunc(containerName, newEnvFromVar, DefaultEnvFromSourceFromMergeFunction)
}
}

func (impl *envFromVarManagerImpl) AddEnvFromVarToInitContainer(initContainerName common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource) {
_ = impl.AddEnvFromVarToInitContainerWithMergeFunc(initContainerName, newEnvFromVar, DefaultEnvFromSourceFromMergeFunction)
}

func (impl *envFromVarManagerImpl) AddEnvFromVarToContainerWithMergeFunc(containerName common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource, mergeFunc EnvFromSourceFromMergeFunction) error {
for id := range impl.podTmpl.Spec.Containers {
if impl.podTmpl.Spec.Containers[id].Name == string(containerName) {
_, err := AddEnvFromSourceFromToContainer(&impl.podTmpl.Spec.Containers[id], newEnvFromVar, mergeFunc)
if err != nil {
return err
}
}
}
return nil
}

func (impl *envFromVarManagerImpl) AddEnvFromVarToInitContainerWithMergeFunc(initContainerName common.AgentContainerName, newEnvFromVar *corev1.EnvFromSource, mergeFunc EnvFromSourceFromMergeFunction) error {
for id := range impl.podTmpl.Spec.InitContainers {
if impl.podTmpl.Spec.InitContainers[id].Name == string(initContainerName) {
_, err := AddEnvFromSourceFromToContainer(&impl.podTmpl.Spec.InitContainers[id], newEnvFromVar, mergeFunc)
if err != nil {
return err
}
}
}
return nil
}

// EnvFromSourceFromMergeFunction signature for corev1.EnvFromSource merge function
type EnvFromSourceFromMergeFunction func(current, newEnv *corev1.EnvFromSource) (*corev1.EnvFromSource, error)

Expand All @@ -23,16 +107,6 @@ func OverrideCurrentEnvFromSourceFromMergeFunction(current, newEnv *corev1.EnvFr
return newEnv.DeepCopy(), nil
}

// IgnoreNewEnvFromSourceFromMergeFunction used when the existing corev1.EnvFromSource needs to be kept.
func IgnoreNewEnvFromSourceFromMergeFunction(current, newEnv *corev1.EnvFromSource) (*corev1.EnvFromSource, error) {
return current.DeepCopy(), nil
}

// ErrorOnMergeAttemptdEnvFromSourceFromMergeFunction used to avoid replacing an existing corev1.EnvFromSource
func ErrorOnMergeAttemptdEnvFromSourceFromMergeFunction(current, newEnv *corev1.EnvFromSource) (*corev1.EnvFromSource, error) {
return nil, errMergeAttempted
}

// AddEnvFromSourceFromToContainer use to add an EnvFromSource to container.
func AddEnvFromSourceFromToContainer(container *corev1.Container, envFromSource *corev1.EnvFromSource, mergeFunc EnvFromSourceFromMergeFunction) ([]corev1.EnvFromSource, error) {
var found bool
Expand Down
Loading

0 comments on commit 5e5ce7e

Please sign in to comment.