diff --git a/pkg/controller/operators/olm/overrides/config.go b/pkg/controller/operators/olm/overrides/config.go index 1db378302f..6727c8b837 100644 --- a/pkg/controller/operators/olm/overrides/config.go +++ b/pkg/controller/operators/olm/overrides/config.go @@ -16,7 +16,7 @@ type operatorConfig struct { logger *logrus.Logger } -func (o *operatorConfig) GetConfigOverrides(ownerCSV ownerutil.Owner) (envVarOverrides []corev1.EnvVar, volumeOverrides []corev1.Volume, volumeMountOverrides []corev1.VolumeMount, tolerationOverrides []corev1.Toleration, resourcesOverride *corev1.ResourceRequirements, nodeSelectorOverride map[string]string, affinity *corev1.Affinity, annotations map[string]string, err error) { +func (o *operatorConfig) GetConfigOverrides(ownerCSV ownerutil.Owner) (envVarOverrides []corev1.EnvVar, envFromOverrides []corev1.EnvFromSource, volumeOverrides []corev1.Volume, volumeMountOverrides []corev1.VolumeMount, tolerationOverrides []corev1.Toleration, resourcesOverride *corev1.ResourceRequirements, nodeSelectorOverride map[string]string, affinity *corev1.Affinity, annotations map[string]string, err error) { list, listErr := o.lister.OperatorsV1alpha1().SubscriptionLister().Subscriptions(ownerCSV.GetNamespace()).List(labels.Everything()) if listErr != nil { err = fmt.Errorf("failed to list subscription namespace=%s - %v", ownerCSV.GetNamespace(), listErr) @@ -35,6 +35,7 @@ func (o *operatorConfig) GetConfigOverrides(ownerCSV ownerutil.Owner) (envVarOve } envVarOverrides = owner.Spec.Config.Env + envFromOverrides = owner.Spec.Config.EnvFrom volumeOverrides = owner.Spec.Config.Volumes volumeMountOverrides = owner.Spec.Config.VolumeMounts tolerationOverrides = owner.Spec.Config.Tolerations diff --git a/pkg/controller/operators/olm/overrides/initializer.go b/pkg/controller/operators/olm/overrides/initializer.go index 983c01709c..33f93f5164 100644 --- a/pkg/controller/operators/olm/overrides/initializer.go +++ b/pkg/controller/operators/olm/overrides/initializer.go @@ -45,7 +45,7 @@ func (d *DeploymentInitializer) initialize(ownerCSV ownerutil.Owner, deployment var envVarOverrides, proxyEnvVar, merged []corev1.EnvVar var err error - envVarOverrides, volumeOverrides, volumeMountOverrides, tolerationOverrides, resourcesOverride, nodeSelectorOverride, affinity, annotations, err := d.config.GetConfigOverrides(ownerCSV) + envVarOverrides, envFromOverrides, volumeOverrides, volumeMountOverrides, tolerationOverrides, resourcesOverride, nodeSelectorOverride, affinity, annotations, err := d.config.GetConfigOverrides(ownerCSV) if err != nil { err = fmt.Errorf("failed to get subscription pod configuration - %v", err) return err @@ -72,6 +72,10 @@ func (d *DeploymentInitializer) initialize(ownerCSV ownerutil.Owner, deployment return fmt.Errorf("failed to inject proxy env variable(s) into deployment spec name=%s - %v", deployment.Name, err) } + if err := inject.InjectEnvFromIntoDeployment(podSpec, envFromOverrides); err != nil { + return fmt.Errorf("failed to inject envFrom variable(s) into deployment spec name=%s - %v", deployment.Name, err) + } + if err = inject.InjectVolumesIntoDeployment(podSpec, volumeOverrides); err != nil { return fmt.Errorf("failed to inject volume(s) into deployment spec name=%s - %v", deployment.Name, err) } diff --git a/pkg/controller/operators/olm/overrides/inject/inject.go b/pkg/controller/operators/olm/overrides/inject/inject.go index 8f09eb353f..e500f5623e 100644 --- a/pkg/controller/operators/olm/overrides/inject/inject.go +++ b/pkg/controller/operators/olm/overrides/inject/inject.go @@ -62,6 +62,45 @@ func mergeEnvVars(containerEnvVars []corev1.EnvVar, newEnvVars []corev1.EnvVar) return merged } +// InjectEnvFromIntoDeployment injects the envFrom variables +// into the container(s) of the given PodSpec. +// +// If any Container in PodSpec already defines an envFrom variable +// as any of the provided envFrom then it will be overwritten. +func InjectEnvFromIntoDeployment(podSpec *corev1.PodSpec, envFromVars []corev1.EnvFromSource) error { + if podSpec == nil { + return errors.New("no pod spec provided") + } + + for i := range podSpec.Containers { + container := &podSpec.Containers[i] + container.EnvFrom = mergeEnvFromVars(container.EnvFrom, envFromVars) + } + + return nil +} + +func mergeEnvFromVars(containerEnvFromVars []corev1.EnvFromSource, newEnvFromVars []corev1.EnvFromSource) []corev1.EnvFromSource { + merged := containerEnvFromVars + + for _, newEnvFromVar := range newEnvFromVars { + if !findEnvFromVar(containerEnvFromVars, newEnvFromVar) { + merged = append(merged, newEnvFromVar) + } + } + + return merged +} + +func findEnvFromVar(envFromVar []corev1.EnvFromSource, newEnvFromVar corev1.EnvFromSource) bool { + for i := range envFromVar { + if reflect.DeepEqual(envFromVar[i], newEnvFromVar) { + return true + } + } + return false +} + // InjectVolumesIntoDeployment injects the provided Volumes // into the container(s) of the given PodSpec. // diff --git a/pkg/controller/operators/olm/overrides/inject/inject_test.go b/pkg/controller/operators/olm/overrides/inject/inject_test.go index fe86f3d458..416214cba2 100644 --- a/pkg/controller/operators/olm/overrides/inject/inject_test.go +++ b/pkg/controller/operators/olm/overrides/inject/inject_test.go @@ -30,6 +30,26 @@ var ( }, } + defaultEnvFromVars = []corev1.EnvFromSource{ + { + Prefix: "test", + }, + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "configmapForTest", + }, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "secretForTest", + }, + }, + }, + } + defaultVolumeMounts = []corev1.VolumeMount{ { Name: "foo", @@ -526,6 +546,171 @@ func TestInjectEnvIntoDeployment(t *testing.T) { } } +func TestInjectEnvFromIntoDeployment(t *testing.T) { + tests := []struct { + name string + podSpec *corev1.PodSpec + envFromVar []corev1.EnvFromSource + expected *corev1.PodSpec + }{ + { + // PodSpec has one container and `EnvFrom` is empty. + // Expected: All env variable(s) specified are injected. + name: "WithContainerHasNoEnvFromVar", + podSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + envFromVar: defaultEnvFromVars, + expected: &corev1.PodSpec{ + Containers: []corev1.Container{ + { + EnvFrom: defaultEnvFromVars, + }, + }, + }, + }, + { + // PodSpec has one container and it has overlapping envFrom var(s). + // Expected: existing duplicate env vars won't be appended in the envFrom. + name: "WithContainerHasOverlappingEnvFromVar", + podSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ + { + EnvFrom: []corev1.EnvFromSource{ + { + Prefix: "test", + }, + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "configmapForTest", + }, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "secretForTest", + }, + }, + }, + }, + }, + }, + }, + envFromVar: defaultEnvFromVars, + expected: &corev1.PodSpec{ + Containers: []corev1.Container{ + { + EnvFrom: []corev1.EnvFromSource{ + { + Prefix: "test", + }, + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "configmapForTest", + }, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "secretForTest", + }, + }, + }, + }, + }, + }, + }, + }, + { + // PodSpec has one container and it has non overlapping envFrom var(s). + // Expected: existing non overlapping env vars are intact. + name: "WithContainerHasNonOverlappingEnvFromVar", + podSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ + { + EnvFrom: []corev1.EnvFromSource{ + { + Prefix: "foo", + }, + }, + }, + }, + }, + envFromVar: defaultEnvFromVars, + expected: &corev1.PodSpec{ + Containers: []corev1.Container{ + { + EnvFrom: append([]corev1.EnvFromSource{ + { + Prefix: "foo", + }, + }, defaultEnvFromVars...), + }, + }, + }, + }, + { + // PodSpec has more than one container(s) + // Expected: All container(s) should be updated as expected. + name: "WithMultipleContainers", + podSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + { + EnvFrom: []corev1.EnvFromSource{ + { + Prefix: "foo", + }, + }, + }, + { + EnvFrom: []corev1.EnvFromSource{ + { + Prefix: "bar", + }, + }, + }, + }, + }, + envFromVar: defaultEnvFromVars, + expected: &corev1.PodSpec{ + Containers: []corev1.Container{ + { + EnvFrom: defaultEnvFromVars, + }, + { + EnvFrom: append([]corev1.EnvFromSource{ + { + Prefix: "foo", + }, + }, defaultEnvFromVars...), + }, + { + EnvFrom: append([]corev1.EnvFromSource{ + { + Prefix: "bar", + }, + }, defaultEnvFromVars...), + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + inject.InjectEnvFromIntoDeployment(tt.podSpec, tt.envFromVar) + assert.Equal(t, tt.expected, tt.podSpec) + }) + } +} + func TestInjectTolerationsIntoDeployment(t *testing.T) { tests := []struct { name string