@@ -3,6 +3,7 @@ package generators
33import (
44 "cmp"
55 "fmt"
6+ "reflect"
67 "slices"
78 "strconv"
89 "strings"
@@ -21,6 +22,7 @@ import (
2122 "github.com/operator-framework/api/pkg/operators/v1alpha1"
2223 registrybundle "github.com/operator-framework/operator-registry/pkg/lib/bundle"
2324
25+ "github.com/operator-framework/operator-controller/internal/operator-controller/config"
2426 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle"
2527 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
2628 "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util"
@@ -98,6 +100,9 @@ func BundleCSVDeploymentGenerator(rv1 *bundle.RegistryV1, opts render.Options) (
98100 ensureCorrectDeploymentCertVolumes (deploymentResource , * secretInfo )
99101 }
100102
103+ // Apply deployment configuration if provided
104+ applyCustomConfigToDeployment (deploymentResource , opts .DeploymentConfig )
105+
101106 objs = append (objs , deploymentResource )
102107 }
103108 return objs , nil
@@ -578,3 +583,214 @@ func getWebhookNamespaceSelector(targetNamespaces []string) *metav1.LabelSelecto
578583 }
579584 return nil
580585}
586+
587+ // applyCustomConfigToDeployment applies the deployment configuration to all containers in the deployment.
588+ // It follows OLMv0 behavior for applying configuration to deployments.
589+ // See https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go
590+ func applyCustomConfigToDeployment (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
591+ if config == nil {
592+ return
593+ }
594+
595+ // Apply all configuration modifications following OLMv0 behavior
596+ applyEnvironmentConfig (deployment , config )
597+ applyEnvironmentFromConfig (deployment , config )
598+ applyVolumeConfig (deployment , config )
599+ applyVolumeMountConfig (deployment , config )
600+ applyTolerationsConfig (deployment , config )
601+ applyResourcesConfig (deployment , config )
602+ applyNodeSelectorConfig (deployment , config )
603+ applyAffinityConfig (deployment , config )
604+ applyAnnotationsConfig (deployment , config )
605+ }
606+
607+ // applyEnvironmentConfig applies environment variables to all containers in the deployment.
608+ // Environment variables from config override existing environment variables with the same name.
609+ // This follows OLMv0 behavior:
610+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L11-L27
611+ func applyEnvironmentConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
612+ if len (config .Env ) == 0 {
613+ return
614+ }
615+
616+ for i := range deployment .Spec .Template .Spec .Containers {
617+ container := & deployment .Spec .Template .Spec .Containers [i ]
618+
619+ // Create a map to track existing env var names for override behavior
620+ existingEnvMap := make (map [string ]int )
621+ for idx , env := range container .Env {
622+ existingEnvMap [env .Name ] = idx
623+ }
624+
625+ // Apply config env vars, overriding existing ones with same name
626+ for _ , configEnv := range config .Env {
627+ if existingIdx , exists := existingEnvMap [configEnv .Name ]; exists {
628+ // Override existing env var
629+ container .Env [existingIdx ] = configEnv
630+ } else {
631+ // Append new env var
632+ container .Env = append (container .Env , configEnv )
633+ }
634+ }
635+ }
636+ }
637+
638+ // applyEnvironmentFromConfig appends EnvFrom sources to all containers in the deployment.
639+ // Duplicate EnvFrom sources are not added.
640+ // This follows OLMv0 behavior:
641+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L65-L81
642+ func applyEnvironmentFromConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
643+ if len (config .EnvFrom ) == 0 {
644+ return
645+ }
646+
647+ for i := range deployment .Spec .Template .Spec .Containers {
648+ container := & deployment .Spec .Template .Spec .Containers [i ]
649+
650+ // Check for duplicates before appending
651+ for _ , configEnvFrom := range config .EnvFrom {
652+ isDuplicate := false
653+ for _ , existingEnvFrom := range container .EnvFrom {
654+ if reflect .DeepEqual (existingEnvFrom , configEnvFrom ) {
655+ isDuplicate = true
656+ break
657+ }
658+ }
659+ if ! isDuplicate {
660+ container .EnvFrom = append (container .EnvFrom , configEnvFrom )
661+ }
662+ }
663+ }
664+ }
665+
666+ // applyVolumeConfig appends volumes to the deployment's pod spec.
667+ // This follows OLMv0 behavior:
668+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L104-L117
669+ func applyVolumeConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
670+ if len (config .Volumes ) == 0 {
671+ return
672+ }
673+
674+ deployment .Spec .Template .Spec .Volumes = append (deployment .Spec .Template .Spec .Volumes , config .Volumes ... )
675+ }
676+
677+ // applyVolumeMountConfig appends volume mounts to all containers in the deployment.
678+ // This follows OLMv0 behavior:
679+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L149-L165
680+ func applyVolumeMountConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
681+ if len (config .VolumeMounts ) == 0 {
682+ return
683+ }
684+
685+ for i := range deployment .Spec .Template .Spec .Containers {
686+ container := & deployment .Spec .Template .Spec .Containers [i ]
687+ container .VolumeMounts = append (container .VolumeMounts , config .VolumeMounts ... )
688+ }
689+ }
690+
691+ // applyTolerationsConfig appends tolerations to the deployment's pod spec.
692+ // Duplicate tolerations are not added.
693+ // This follows OLMv0 behavior:
694+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L197-L209
695+ func applyTolerationsConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
696+ if len (config .Tolerations ) == 0 {
697+ return
698+ }
699+
700+ // Check for duplicates before appending
701+ for _ , configToleration := range config .Tolerations {
702+ isDuplicate := false
703+ for _ , existingToleration := range deployment .Spec .Template .Spec .Tolerations {
704+ if reflect .DeepEqual (existingToleration , configToleration ) {
705+ isDuplicate = true
706+ break
707+ }
708+ }
709+ if ! isDuplicate {
710+ deployment .Spec .Template .Spec .Tolerations = append (deployment .Spec .Template .Spec .Tolerations , configToleration )
711+ }
712+ }
713+ }
714+
715+ // applyResourcesConfig applies resource requirements to all containers in the deployment.
716+ // This completely replaces existing resource requirements.
717+ // This follows OLMv0 behavior:
718+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L236-L255
719+ func applyResourcesConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
720+ if config .Resources == nil {
721+ return
722+ }
723+
724+ for i := range deployment .Spec .Template .Spec .Containers {
725+ container := & deployment .Spec .Template .Spec .Containers [i ]
726+ container .Resources = * config .Resources
727+ }
728+ }
729+
730+ // applyNodeSelectorConfig applies node selector to the deployment's pod spec.
731+ // This completely replaces existing node selector.
732+ // This follows OLMv0 behavior:
733+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L257-L271
734+ func applyNodeSelectorConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
735+ if config .NodeSelector == nil {
736+ return
737+ }
738+
739+ deployment .Spec .Template .Spec .NodeSelector = config .NodeSelector
740+ }
741+
742+ // applyAffinityConfig applies affinity configuration to the deployment's pod spec.
743+ // This selectively overrides non-nil affinity sub-attributes.
744+ // This follows OLMv0 behavior:
745+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L273-L341
746+ func applyAffinityConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
747+ if config .Affinity == nil {
748+ return
749+ }
750+
751+ if deployment .Spec .Template .Spec .Affinity == nil {
752+ deployment .Spec .Template .Spec .Affinity = & corev1.Affinity {}
753+ }
754+
755+ if config .Affinity .NodeAffinity != nil {
756+ deployment .Spec .Template .Spec .Affinity .NodeAffinity = config .Affinity .NodeAffinity
757+ }
758+
759+ if config .Affinity .PodAffinity != nil {
760+ deployment .Spec .Template .Spec .Affinity .PodAffinity = config .Affinity .PodAffinity
761+ }
762+
763+ if config .Affinity .PodAntiAffinity != nil {
764+ deployment .Spec .Template .Spec .Affinity .PodAntiAffinity = config .Affinity .PodAntiAffinity
765+ }
766+ }
767+
768+ // applyAnnotationsConfig applies annotations to the deployment and its pod template.
769+ // Existing deployment and pod annotations take precedence over config annotations (no override).
770+ // This follows OLMv0 behavior:
771+ // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L343-L378
772+ func applyAnnotationsConfig (deployment * appsv1.Deployment , config * config.DeploymentConfig ) {
773+ if len (config .Annotations ) == 0 {
774+ return
775+ }
776+
777+ // Apply to deployment metadata
778+ if deployment .Annotations == nil {
779+ deployment .Annotations = make (map [string ]string )
780+ }
781+ for key , value := range config .Annotations {
782+ if _ , exists := deployment .Annotations [key ]; ! exists {
783+ deployment .Annotations [key ] = value
784+ }
785+ }
786+
787+ // Apply to pod template metadata
788+ if deployment .Spec .Template .Annotations == nil {
789+ deployment .Spec .Template .Annotations = make (map [string ]string )
790+ }
791+ for key , value := range config .Annotations {
792+ if _ , exists := deployment .Spec .Template .Annotations [key ]; ! exists {
793+ deployment .Spec .Template .Annotations [key ] = value
794+ }
795+ }
796+ }
0 commit comments