Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 83
✨Add DeploymentConfig support to registry+v1 bundle renderer#2469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3,6 +3,7 @@ package generators | ||
| import ( | ||
| "cmp" | ||
| "fmt" | ||
| "reflect" | ||
| "slices" | ||
| "strconv" | ||
| "strings" | ||
| @@ -21,6 +22,7 @@ import ( | ||
| "github.com/operator-framework/api/pkg/operators/v1alpha1" | ||
| registrybundle "github.com/operator-framework/operator-registry/pkg/lib/bundle" | ||
| "github.com/operator-framework/operator-controller/internal/operator-controller/config" | ||
| "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle" | ||
| "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render" | ||
| "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util" | ||
| @@ -98,6 +100,9 @@ func BundleCSVDeploymentGenerator(rv1 *bundle.RegistryV1, opts render.Options) ( | ||
| ensureCorrectDeploymentCertVolumes(deploymentResource, *secretInfo) | ||
| } | ||
| // Apply deployment configuration if provided | ||
| applyCustomConfigToDeployment(deploymentResource, opts.DeploymentConfig) | ||
| objs = append(objs, deploymentResource) | ||
| } | ||
| return objs, nil | ||
| @@ -578,3 +583,214 @@ func getWebhookNamespaceSelector(targetNamespaces []string) *metav1.LabelSelecto | ||
| } | ||
| return nil | ||
| } | ||
| // applyCustomConfigToDeployment applies the deployment configuration to all containers in the deployment. | ||
| // It follows OLMv0 behavior for applying configuration to deployments. | ||
| // See https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go | ||
| func applyCustomConfigToDeployment(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if config == nil { | ||
| return | ||
| } | ||
| // Apply all configuration modifications following OLMv0 behavior | ||
| applyEnvironmentConfig(deployment, config) | ||
| applyEnvironmentFromConfig(deployment, config) | ||
| applyVolumeConfig(deployment, config) | ||
| applyVolumeMountConfig(deployment, config) | ||
| applyTolerationsConfig(deployment, config) | ||
| applyResourcesConfig(deployment, config) | ||
| applyNodeSelectorConfig(deployment, config) | ||
| applyAffinityConfig(deployment, config) | ||
| applyAnnotationsConfig(deployment, config) | ||
| } | ||
| // applyEnvironmentConfig applies environment variables to all containers in the deployment. | ||
| // Environment variables from config override existing environment variables with the same name. | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L11-L27 | ||
| func applyEnvironmentConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if len(config.Env) == 0 { | ||
| return | ||
| } | ||
| for i := range deployment.Spec.Template.Spec.Containers { | ||
| container := &deployment.Spec.Template.Spec.Containers[i] | ||
| // Create a map to track existing env var names for override behavior | ||
| existingEnvMap := make(map[string]int) | ||
| for idx, env := range container.Env { | ||
| existingEnvMap[env.Name] = idx | ||
| } | ||
| // Apply config env vars, overriding existing ones with same name | ||
| for _, configEnv := range config.Env { | ||
| if existingIdx, exists := existingEnvMap[configEnv.Name]; exists { | ||
| // Override existing env var | ||
| container.Env[existingIdx] = configEnv | ||
| } else { | ||
| // Append new env var | ||
| container.Env = append(container.Env, configEnv) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // applyEnvironmentFromConfig appends EnvFrom sources to all containers in the deployment. | ||
| // Duplicate EnvFrom sources are not added. | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L65-L81 | ||
| func applyEnvironmentFromConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if len(config.EnvFrom) == 0 { | ||
| return | ||
| } | ||
| for i := range deployment.Spec.Template.Spec.Containers { | ||
| container := &deployment.Spec.Template.Spec.Containers[i] | ||
| // Check for duplicates before appending | ||
| for _, configEnvFrom := range config.EnvFrom { | ||
| isDuplicate := false | ||
| for _, existingEnvFrom := range container.EnvFrom { | ||
| if reflect.DeepEqual(existingEnvFrom, configEnvFrom) { | ||
| isDuplicate = true | ||
| break | ||
| } | ||
| } | ||
| if !isDuplicate { | ||
| container.EnvFrom = append(container.EnvFrom, configEnvFrom) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // applyVolumeConfig appends volumes to the deployment's pod spec. | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L104-L117 | ||
| func applyVolumeConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if len(config.Volumes) == 0 { | ||
| return | ||
| } | ||
| deployment.Spec.Template.Spec.Volumes = append(deployment.Spec.Template.Spec.Volumes, config.Volumes...) | ||
| } | ||
| // applyVolumeMountConfig appends volume mounts to all containers in the deployment. | ||
| // This follows OLMv0 behavior: | ||
anik120 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L149-L165 | ||
| func applyVolumeMountConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if len(config.VolumeMounts) == 0 { | ||
| return | ||
| } | ||
| for i := range deployment.Spec.Template.Spec.Containers { | ||
| container := &deployment.Spec.Template.Spec.Containers[i] | ||
anik120 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| container.VolumeMounts = append(container.VolumeMounts, config.VolumeMounts...) | ||
| } | ||
| } | ||
| // applyTolerationsConfig appends tolerations to the deployment's pod spec. | ||
| // Duplicate tolerations are not added. | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L197-L209 | ||
| func applyTolerationsConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if len(config.Tolerations) == 0 { | ||
| return | ||
| } | ||
| // Check for duplicates before appending | ||
| for _, configToleration := range config.Tolerations { | ||
| isDuplicate := false | ||
| for _, existingToleration := range deployment.Spec.Template.Spec.Tolerations { | ||
| if reflect.DeepEqual(existingToleration, configToleration) { | ||
| isDuplicate = true | ||
| break | ||
| } | ||
| } | ||
| if !isDuplicate { | ||
| deployment.Spec.Template.Spec.Tolerations = append(deployment.Spec.Template.Spec.Tolerations, configToleration) | ||
| } | ||
| } | ||
| } | ||
| // applyResourcesConfig applies resource requirements to all containers in the deployment. | ||
| // This completely replaces existing resource requirements. | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L236-L255 | ||
| func applyResourcesConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if config.Resources == nil { | ||
| return | ||
| } | ||
| for i := range deployment.Spec.Template.Spec.Containers { | ||
| container := &deployment.Spec.Template.Spec.Containers[i] | ||
| container.Resources = *config.Resources | ||
| } | ||
| } | ||
| // applyNodeSelectorConfig applies node selector to the deployment's pod spec. | ||
| // This completely replaces existing node selector. | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L257-L271 | ||
| func applyNodeSelectorConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if config.NodeSelector == nil { | ||
| return | ||
| } | ||
| deployment.Spec.Template.Spec.NodeSelector = config.NodeSelector | ||
| } | ||
| // applyAffinityConfig applies affinity configuration to the deployment's pod spec. | ||
| // This selectively overrides non-nil affinity sub-attributes. | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L273-L341 | ||
| func applyAffinityConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if config.Affinity == nil { | ||
| return | ||
| } | ||
| if deployment.Spec.Template.Spec.Affinity == nil { | ||
| deployment.Spec.Template.Spec.Affinity = &corev1.Affinity{} | ||
| } | ||
| if config.Affinity.NodeAffinity != nil { | ||
| deployment.Spec.Template.Spec.Affinity.NodeAffinity = config.Affinity.NodeAffinity | ||
| } | ||
| if config.Affinity.PodAffinity != nil { | ||
| deployment.Spec.Template.Spec.Affinity.PodAffinity = config.Affinity.PodAffinity | ||
| } | ||
| if config.Affinity.PodAntiAffinity != nil { | ||
| deployment.Spec.Template.Spec.Affinity.PodAntiAffinity = config.Affinity.PodAntiAffinity | ||
| } | ||
| } | ||
| // applyAnnotationsConfig applies annotations to the deployment and its pod template. | ||
| // Existing deployment and pod annotations take precedence over config annotations (no override). | ||
| // This follows OLMv0 behavior: | ||
| // https://github.com/operator-framework/operator-lifecycle-manager/blob/v0.39.0/pkg/controller/operators/olm/overrides/inject/inject.go#L343-L378 | ||
| func applyAnnotationsConfig(deployment *appsv1.Deployment, config *config.DeploymentConfig) { | ||
| if len(config.Annotations) == 0 { | ||
| return | ||
| } | ||
| // Apply to deployment metadata | ||
| if deployment.Annotations == nil { | ||
| deployment.Annotations = make(map[string]string) | ||
| } | ||
| for key, value := range config.Annotations { | ||
| if _, exists := deployment.Annotations[key]; !exists { | ||
| deployment.Annotations[key] = value | ||
| } | ||
| } | ||
| // Apply to pod template metadata | ||
| if deployment.Spec.Template.Annotations == nil { | ||
| deployment.Spec.Template.Annotations = make(map[string]string) | ||
| } | ||
| for key, value := range config.Annotations { | ||
| if _, exists := deployment.Spec.Template.Annotations[key]; !exists { | ||
| deployment.Spec.Template.Annotations[key] = value | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we weren't going to use SubscriptionConfig because of the extra selector field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the RFC:
Notice that there's no
applySelectorConfig()function, so we're just ignoring that field.With our wish to reuse v0 SubscriptionConfig for maintainability, we carry over nuance/s into v1. Silently ignoring the selector field being one of them.
We have to remember to document this explicitly for our users everywhere for added clarity.