diff --git a/cmd/deploy.go b/cmd/deploy.go index c866cb9c..ab61ac7e 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -31,6 +31,7 @@ Examples: cmd.Flags().BoolVar(&helm, "helm", false, "Deploy using Helm charts instead of operator") cmd.Flags().BoolVar(&olm, "olm", false, "Deploy operator via OLM (requires OLM installed)") + cmd.Flags().BoolVar(&deployOperator, "deploy-operator", true, "Deploy and check operator (set to false to skip operator deployment/checks)") cmd.Flags().BoolVar(&portForwarding, "port-forwarding", false, "Enable localhost port-forward for Central") cmd.Flags().BoolVar(&pauseReconciliation, "pause-reconciliation", false, "Pause reconciliation after deployment") cmd.Flags().StringVar(&overrideFile, "override", "", "Path to YAML file with overrides") @@ -91,6 +92,15 @@ func runDeploy(cmd *cobra.Command, args []string) error { os.Setenv("KUBECONFIG", "/kubeconfig") } + // Validate flag combinations early + if helm && olm { + return errors.New("cannot use both --helm and --olm flags together") + } + + if !deployOperator && olm { + return errors.New("cannot use --deploy-operator=false with --olm (OLM requires operator deployment)") + } + d, err := deployer.New(log, overrideFile, overrideSetExpressions) if err != nil { return fmt.Errorf("failed to create deployer: %w", err) @@ -107,10 +117,6 @@ func runDeploy(cmd *cobra.Command, args []string) error { d.SetEnvrcFile(envrc) } - if helm && olm { - return errors.New("cannot use both --helm and --olm flags together") - } - if helm { if err := d.SetUseHelm(true); err != nil { return err @@ -123,6 +129,8 @@ func runDeploy(cmd *cobra.Command, args []string) error { } } + d.SetDeployOperator(deployOperator) + d.SetVerbose(verbose) d.SetEarlyReadiness(earlyReadiness) d.SetPortForwardingEnabled(portForwardEnabledFinal) diff --git a/cmd/main.go b/cmd/main.go index 7c96185a..096ff9f0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -13,6 +13,7 @@ var ( earlyReadiness bool helm bool olm bool + deployOperator bool portForwarding bool pauseReconciliation bool overrideFile string diff --git a/internal/deployer/deploy_via_operator.go b/internal/deployer/deploy_via_operator.go index abe953a8..fcaca65e 100644 --- a/internal/deployer/deploy_via_operator.go +++ b/internal/deployer/deploy_via_operator.go @@ -16,6 +16,13 @@ import ( // ensureOperatorDeployed ensures the operator is deployed with the correct version and mode func (d *Deployer) ensureOperatorDeployed(ctx context.Context) error { + // Skip operator deployment/checks if flag is set to false + if !d.shouldDeployOperator { + d.logger.Info("ℹ️ Skipping operator deployment checks (--deploy-operator=false)") + d.logger.Info(" Assuming operator is already running...") + return nil + } + if err := d.ensureCRDsInstalled(ctx); err != nil { return fmt.Errorf("failed to ensure CRDs installed: %w", err) } @@ -263,13 +270,14 @@ func (d *Deployer) createCentralCR(resources, exposure string) (map[string]inter } resourcesOverlay := d.getCentralResourcesOperator(resources) + imageOverlay := d.getCentralImageOverlays() overrides, err := GetOverrides(d.overrideFile, d.overrideSetExpressions) if err != nil { return nil, fmt.Errorf("failed construct Central CR overrides: %w", err) } - merged := helpers.MergeMaps(base, resourcesOverlay, overrides) + merged := helpers.MergeMaps(base, resourcesOverlay, imageOverlay, overrides) return merged, nil } @@ -337,6 +345,43 @@ func (d *Deployer) getCentralResourcesOperator(resourcesName string) map[string] return resources } +// getCentralImageOverlays returns image tag overlays for Central components +func (d *Deployer) getCentralImageOverlays() map[string]interface{} { + if d.mainImageTag == "" { + return map[string]interface{}{} + } + + // Create overlays to set the image tag for all Central deployments. + overlays := []map[string]interface{}{ + d.mainImageOverlays("Deployment", "central", map[string]string{ + "central": "main", + }), + d.mainImageOverlays("Deployment", "config-controller", map[string]string{ + "manager": "main", + }), + d.mainImageOverlays("Deployment", "central-db", map[string]string{ + "init:init-db": "central-db", + "central-db": "central-db", + }), + d.mainImageOverlays("Deployment", "scanner-v4-indexer", map[string]string{ + "indexer": "scanner-v4", + }), + d.mainImageOverlays("Deployment", "scanner-v4-matcher", map[string]string{ + "matcher": "scanner-v4", + }), + d.mainImageOverlays("Deployment", "scanner-v4-db", map[string]string{ + "init:init-db": "scanner-v4-db", + "db": "scanner-v4-db", + }), + } + + return map[string]interface{}{ + "spec": map[string]interface{}{ + "overlays": overlays, + }, + } +} + // getCentralExposureConfig returns the exposure configuration func (d *Deployer) getCentralExposureConfig(exposure string) map[string]interface{} { switch exposure { @@ -642,13 +687,14 @@ func (d *Deployer) createSecuredClusterCR(clusterName, resources string) (map[st } resourcesOverlay := d.getSecuredClusterResourcesOperator(resources) + imageOverlay := d.getSecuredClusterImageOverlays() overrides, err := GetOverrides(d.overrideFile, d.overrideSetExpressions) if err != nil { return nil, fmt.Errorf("failed construct Central CR overrides: %w", err) } - merged := helpers.MergeMaps(base, resourcesOverlay, overrides) + merged := helpers.MergeMaps(base, resourcesOverlay, imageOverlay, overrides) return merged, nil } @@ -694,6 +740,79 @@ func (d *Deployer) getSecuredClusterResourcesOperator(resourcesName string) map[ return resources } +func (d *Deployer) mainImageOverlays(kind, name string, containerImages map[string]string) map[string]interface{} { + patches := make([]map[string]interface{}, 0, len(containerImages)) + for containerName, containerImage := range containerImages { + containerType := "containers" + containerComponents := strings.SplitN(containerName, ":", 2) + if len(containerComponents) == 2 { + if containerComponents[0] == "init" { + containerType = "initContainers" + containerName = containerComponents[1] + } else { + panic(fmt.Sprintf("invalid container type: %s", containerComponents[0])) + } + } + patchPath := fmt.Sprintf("spec.template.spec.%s[name:%s].image", containerType, containerName) + patches = append(patches, map[string]interface{}{ + "path": patchPath, + "value": fmt.Sprintf("quay.io/rhacs-eng/%s:%s", containerImage, d.mainImageTag), + }) + } + return map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": kind, + "name": name, + "optional": true, + "patches": patches, + } +} + +// getSecuredClusterImageOverlays returns image tag overlays for SecuredCluster components +func (d *Deployer) getSecuredClusterImageOverlays() map[string]interface{} { + if d.mainImageTag == "" { + return map[string]interface{}{} + } + + // Create overlays to set the image tag for all SecuredCluster deployments. + overlays := []map[string]interface{}{ + d.mainImageOverlays("Deployment", "sensor", map[string]string{ + "init:crs": "main", + "init:init-tls-certs": "main", + "sensor": "main", + }), + d.mainImageOverlays("Deployment", "admission-control", map[string]string{ + "init:init-tls-certs": "main", + "admission-control": "main", + }), + d.mainImageOverlays("DaemonSet", "collector", map[string]string{ + "init:init-tls-certs": "main", + "compliance": "main", + }), + d.mainImageOverlays("Deployment", "scanner-v4-indexer", map[string]string{ + "init:init-tls-certs": "main", + "indexer": "scanner-v4", + }), + d.mainImageOverlays("Deployment", "scanner-v4-db", map[string]string{ + "init:init-tls-certs": "main", + "init:init-db": "scanner-v4-db", + "db": "scanner-v4-db", + }), + d.mainImageOverlays("Deployment", "scanner", map[string]string{ + "init:init-tls-certs": "main", + }), + d.mainImageOverlays("Deployment", "scanner-db", map[string]string{ + "init:init-tls-certs": "main", + }), + } + + return map[string]interface{}{ + "spec": map[string]interface{}{ + "overlays": overlays, + }, + } +} + // applySecuredClusterCR applies the SecuredCluster CR to the cluster func (d *Deployer) applySecuredClusterCR(ctx context.Context, cr map[string]interface{}) error { d.logger.Info("Applying SecuredCluster custom resource") diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index e64cf4e6..0ec50c47 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -55,6 +55,7 @@ type Deployer struct { envrcFile string useHelm bool useOLM bool + shouldDeployOperator bool verbose bool earlyReadiness bool dockerCreds *dockerauth.Credentials @@ -84,6 +85,7 @@ func New(log *logger.Logger, overrideFile string, overrideSetExpressions []strin exposure: defaultExposure, overrideFile: overrideFile, overrideSetExpressions: overrideSetExpressions, + shouldDeployOperator: true, } d.dockerAuth = dockerauth.New(log) @@ -553,6 +555,10 @@ func (d *Deployer) removePauseReconcileAnnotation(ctx context.Context, resourceT } } +func (d *Deployer) SetDeployOperator(deployOperator bool) { + d.shouldDeployOperator = deployOperator +} + func (d *Deployer) GetDeploymentInfo() (endpoint, password, caCertFile, kubeContext, exposure string) { return d.centralEndpoint, d.centralPassword, d.roxCACertFile, d.kubeContext, d.exposure }