diff --git a/cmd/deploy.go b/cmd/deploy.go index 98f5c616..d1216b02 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -132,11 +132,41 @@ func runDeploy(cmd *cobra.Command, args []string) error { return errors.New("cannot use --deploy-operator=false with --olm (OLM requires operator deployment)") } - d, err := deployer.New(log, overrideFile, overrideSetExpressions) + d, err := deployer.New(log) if err != nil { return fmt.Errorf("failed to create deployer: %w", err) } + if overrideFile != "" { + var err error + switch component { + case "both", "all": + err = d.SetCombinedOverrideFile(overrideFile) + case "central": + err = d.SetCentralOverrideFile(overrideFile) + case "secured-cluster": + err = d.SetSecuredClusterOverrideFile(overrideFile) + } + if err != nil { + return fmt.Errorf("failed to set override file: %w", err) + } + } + + if len(overrideSetExpressions) > 0 { + var err error + switch component { + case "both", "all": + err = d.SetCombinedOverrideSetExpressions(overrideSetExpressions) + case "central": + err = d.SetCentralOverrideSetExpressions(overrideSetExpressions) + case "secured-cluster": + err = d.SetSecuredClusterOverrideSetExpressions(overrideSetExpressions) + } + if err != nil { + return fmt.Errorf("failed to set override set expressions: %w", err) + } + } + switch component { case "central", "both", "all": d.PrintCentralDeploymentSummary() diff --git a/cmd/teardown.go b/cmd/teardown.go index d256a1ad..7e7fb060 100644 --- a/cmd/teardown.go +++ b/cmd/teardown.go @@ -41,7 +41,7 @@ func runTeardown(cmd *cobra.Command, args []string) error { log.Infof("Tearing down %s", component) - d, err := deployer.New(log, "", []string{}) + d, err := deployer.New(log) if err != nil { return fmt.Errorf("failed to create deployer: %w", err) } diff --git a/internal/deployer/deploy_via_helm.go b/internal/deployer/deploy_via_helm.go index 5e11b1ff..3d4fff77 100644 --- a/internal/deployer/deploy_via_helm.go +++ b/internal/deployer/deploy_via_helm.go @@ -259,12 +259,7 @@ func (d *Deployer) createCentralValues(resourcesName, exposure string) (map[stri resourcesOverlay := d.getCentralResourcesHelm(resourcesName) - overrides, err := GetOverrides(d.overrideFile, d.overrideSetExpressions) - if err != nil { - return nil, fmt.Errorf("failed construct Central Helm values overrides: %w", err) - } - - merged := helpers.MergeMaps(base, imageSettings, resourcesOverlay, overrides) + merged := helpers.MergeMaps(base, imageSettings, resourcesOverlay, d.centralOverrides) return merged, nil } @@ -292,12 +287,7 @@ func (d *Deployer) createSecuredClusterValues(clusterName, resources string) (ma resourcesOverlay := d.getSecuredClusterResourcesHelm(resources) - overrides, err := GetOverrides(d.overrideFile, d.overrideSetExpressions) - if err != nil { - return nil, fmt.Errorf("failed construct Central Helm values overrides: %w", err) - } - - merged := helpers.MergeMaps(base, imageSettings, resourcesOverlay, overrides) + merged := helpers.MergeMaps(base, imageSettings, resourcesOverlay, d.securedClusterOverrides) return merged, nil } @@ -553,8 +543,3 @@ func (d *Deployer) deleteCRDs(ctx context.Context) { IgnoreErrors: true, }) } - -// SetOverrideFile sets the path to the override values file -func (d *Deployer) SetOverrideFile(path string) { - d.overrideFile = path -} diff --git a/internal/deployer/deploy_via_operator.go b/internal/deployer/deploy_via_operator.go index bdcac6c0..2151adbf 100644 --- a/internal/deployer/deploy_via_operator.go +++ b/internal/deployer/deploy_via_operator.go @@ -263,12 +263,7 @@ func (d *Deployer) createCentralCR(resources, exposure string) (map[string]inter d.logger.Infof("Using Central resource profile: %s", resources) resourcesOverlay := d.getCentralResourcesOperator(resources) - 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, d.centralOverrides) return merged, nil } @@ -667,12 +662,7 @@ func (d *Deployer) createSecuredClusterCR(clusterName, resources string) (map[st resourcesOverlay := d.getSecuredClusterResourcesOperator(resources) - 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, d.securedClusterOverrides) return merged, nil } diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 861925c8..adece06e 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -11,6 +11,7 @@ import ( "time" "github.com/fatih/color" + "gopkg.in/yaml.v3" "github.com/stackrox/roxie/internal/clusterdefaults" "github.com/stackrox/roxie/internal/dockerauth" @@ -89,36 +90,36 @@ var ( // Deployer is the base deployer for ACS type Deployer struct { - logger *logger.Logger - startTime time.Time - dockerAuth *dockerauth.DockerAuth - imageCache *imagecache.ImageCache - portForward *portforward.Manager - clusterDefaults *clusterdefaults.Manager - kubectl string - roxctlVersion string - centralNamespace string - sensorNamespace string - mainImageTag string - operatorTag string - centralEndpoint string - centralPassword string - roxCACertFile string - kubeContext string - portForwardEnabled bool - pauseReconciliation bool - exposure string - overrideFile string - overrideSetExpressions []string - envrcFile string - useHelm bool - useOLM bool - useKonflux bool - shouldDeployOperator bool - verbose bool - earlyReadiness bool - dockerCreds *dockerauth.Credentials - clusterResourceKinds map[string]struct{} + logger *logger.Logger + startTime time.Time + dockerAuth *dockerauth.DockerAuth + imageCache *imagecache.ImageCache + portForward *portforward.Manager + clusterDefaults *clusterdefaults.Manager + kubectl string + roxctlVersion string + centralNamespace string + sensorNamespace string + mainImageTag string + operatorTag string + centralEndpoint string + centralPassword string + roxCACertFile string + kubeContext string + portForwardEnabled bool + pauseReconciliation bool + exposure string + centralOverrides map[string]interface{} + securedClusterOverrides map[string]interface{} + envrcFile string + useHelm bool + useOLM bool + useKonflux bool + shouldDeployOperator bool + verbose bool + earlyReadiness bool + dockerCreds *dockerauth.Credentials + clusterResourceKinds map[string]struct{} } type ResourceKindWithName struct { @@ -308,7 +309,137 @@ func (d *Deployer) deleteSecuredClusterResources(ctx context.Context, wait bool) return nil } -func New(log *logger.Logger, overrideFile string, overrideSetExpressions []string) (*Deployer, error) { +var ( + centralOverridePrefix = "central" + securedClusterOverridePrefix = "securedCluster" +) + +func unmarshalYamlFile(filePath string) (map[string]interface{}, error) { + rawContent, err := os.ReadFile(filePath) + if err != nil { + return nil, fmt.Errorf("failed to read override file: %w", err) + } + var content map[string]interface{} + if err := yaml.Unmarshal(rawContent, &content); err != nil { + return nil, fmt.Errorf("failed to parse override file: %w", err) + } + return content, nil +} + +func (d *Deployer) SetCombinedOverrideFile(overrideFile string) error { + overrides, err := unmarshalYamlFile(overrideFile) + if err != nil { + return fmt.Errorf("failed to unmarshal override file: %w", err) + } + + for key, value := range overrides { + switch key { + case centralOverridePrefix: + d.centralOverrides = value.(map[string]interface{}) + case securedClusterOverridePrefix: + d.securedClusterOverrides = value.(map[string]interface{}) + default: + d.logger.Errorf("override file contains key %q; combined deployments require extra nesting under 'central' or 'securedCluster'", key) + return fmt.Errorf("unexpected key %q in override file", key) + } + } + + return nil +} + +// Returns remaining set expressions. +func setOverrideSetExpressions(overrides map[string]interface{}, prefix string, overrideSetExpressions []string) ([]string, error) { + remainingSetExpressions := make([]string, 0) + for _, expr := range overrideSetExpressions { + parts := splitAtFirstEquals(expr) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid override expression '%s': expected format 'key.path=value'", expr) + } + key := parts[0] + if prefix != "" { + if !strings.HasPrefix(parts[0], prefix) { + remainingSetExpressions = append(remainingSetExpressions, expr) + continue + } + key = strings.TrimPrefix(key, prefix+".") + } + var val interface{} + if err := yaml.Unmarshal([]byte(parts[1]), &val); err != nil { + return nil, fmt.Errorf("failed to unmarshal value '%s' for key '%s': %w", parts[1], key, err) + } + if err := setNestedValue(overrides, key, val); err != nil { + return nil, fmt.Errorf("failed to set value for key '%s': %w", key, err) + } + } + + return remainingSetExpressions, nil +} + +func (d *Deployer) SetCombinedOverrideSetExpressions(overrideSetExpressions []string) error { + if d.centralOverrides == nil { + d.centralOverrides = make(map[string]interface{}) + } + if d.securedClusterOverrides == nil { + d.securedClusterOverrides = make(map[string]interface{}) + } + + remainingSetExpressions, err := setOverrideSetExpressions(d.centralOverrides, centralOverridePrefix, overrideSetExpressions) + if err != nil { + return fmt.Errorf("failed to set central override set expressions: %w", err) + } + remainingSetExpressions, err = setOverrideSetExpressions(d.securedClusterOverrides, securedClusterOverridePrefix, remainingSetExpressions) + if err != nil { + return fmt.Errorf("failed to set secured cluster override set expressions: %w", err) + } + + if len(remainingSetExpressions) > 0 { + return fmt.Errorf("some override expressions were not properly prefixed with 'central.' or 'securedCluster.': %v", remainingSetExpressions) + } + + return nil +} + +func (d *Deployer) SetCentralOverrideFile(overrideYaml string) error { + centralOverrides, err := unmarshalYamlFile(overrideYaml) + if err != nil { + return fmt.Errorf("failed to unmarshal override file: %w", err) + } + d.centralOverrides = centralOverrides + return nil +} + +func (d *Deployer) SetCentralOverrideSetExpressions(overrideSetExpressions []string) error { + if d.centralOverrides == nil { + d.centralOverrides = make(map[string]interface{}) + } + _, err := setOverrideSetExpressions(d.centralOverrides, "", overrideSetExpressions) + if err != nil { + return fmt.Errorf("failed to set central override set expressions: %w", err) + } + return nil +} + +func (d *Deployer) SetSecuredClusterOverrideFile(overrideYaml string) error { + securedClusterOverrides, err := unmarshalYamlFile(overrideYaml) + if err != nil { + return fmt.Errorf("failed to unmarshal override file: %w", err) + } + d.securedClusterOverrides = securedClusterOverrides + return nil +} + +func (d *Deployer) SetSecuredClusterOverrideSetExpressions(overrideSetExpressions []string) error { + if d.securedClusterOverrides == nil { + d.securedClusterOverrides = make(map[string]interface{}) + } + _, err := setOverrideSetExpressions(d.securedClusterOverrides, "", overrideSetExpressions) + if err != nil { + return fmt.Errorf("failed to set secured cluster override set expressions: %w", err) + } + return nil +} + +func New(log *logger.Logger) (*Deployer, error) { // Check required tools first if err := checkRequiredTools(); err != nil { return nil, err @@ -322,16 +453,14 @@ func New(log *logger.Logger, overrideFile string, overrideSetExpressions []strin kubectl := getKubectl() d := &Deployer{ - logger: log, - startTime: time.Now(), - kubectl: kubectl, - roxctlVersion: roxctlVersion, - centralNamespace: centralNamespace, - sensorNamespace: sensorNamespace, - exposure: defaultExposure, - overrideFile: overrideFile, - overrideSetExpressions: overrideSetExpressions, - shouldDeployOperator: true, + logger: log, + startTime: time.Now(), + kubectl: kubectl, + roxctlVersion: roxctlVersion, + centralNamespace: centralNamespace, + sensorNamespace: sensorNamespace, + exposure: defaultExposure, + shouldDeployOperator: true, } d.dockerAuth = dockerauth.New(log) diff --git a/internal/deployer/override.go b/internal/deployer/override.go index ef10d088..f44890d3 100644 --- a/internal/deployer/override.go +++ b/internal/deployer/override.go @@ -2,49 +2,9 @@ package deployer import ( "fmt" - "os" "strings" - - "gopkg.in/yaml.v3" ) -func GetOverrides(overrideFile string, overrideSetExpressions []string) (map[string]interface{}, error) { - overrides := make(map[string]interface{}) - if overrideFile != "" { - content, err := os.ReadFile(overrideFile) - if err != nil { - return nil, fmt.Errorf("failed to read override file: %w", err) - } - if err := yaml.Unmarshal(content, &overrides); err != nil { - return nil, fmt.Errorf("failed to parse override file: %w", err) - } - } - - for _, expr := range overrideSetExpressions { - // Split expr at '=' to get path and value - parts := splitAtFirstEquals(expr) - if len(parts) != 2 { - return nil, fmt.Errorf("invalid override expression '%s': expected format 'key.path=value'", expr) - } - - path := parts[0] - valueStr := parts[1] - - // Parse the value as YAML - var value interface{} - if err := yaml.Unmarshal([]byte(valueStr), &value); err != nil { - return nil, fmt.Errorf("failed to parse value '%s' as YAML: %w", valueStr, err) - } - - // Set the value at the specified path - if err := setNestedValue(overrides, path, value); err != nil { - return nil, fmt.Errorf("failed to set override for path '%s': %w", path, err) - } - } - - return overrides, nil -} - // splitAtFirstEquals splits a string at the first '=' character // Returns [key, value] or just [expr] if no '=' found func splitAtFirstEquals(expr string) []string {