From 4687e3528b85dee545b7775e99d030c96a655252 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 17 Dec 2025 23:00:47 +0100 Subject: [PATCH 1/4] New functions for feature flags --- internal/deployer/feature_flags.go | 89 ++++++++ internal/deployer/feature_flags_test.go | 274 ++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 internal/deployer/feature_flags.go create mode 100644 internal/deployer/feature_flags_test.go diff --git a/internal/deployer/feature_flags.go b/internal/deployer/feature_flags.go new file mode 100644 index 00000000..eaaa3b2b --- /dev/null +++ b/internal/deployer/feature_flags.go @@ -0,0 +1,89 @@ +package deployer + +import ( + "fmt" + "strconv" + "strings" + + corev1 "k8s.io/api/core/v1" +) + +type FeatureFlags = *FeatureFlagsStruct + +type FeatureFlagsStruct struct { + m map[string]bool +} + +func NewFeatureFlags() FeatureFlags { + return &FeatureFlagsStruct{ + m: make(map[string]bool), + } +} + +func (f FeatureFlags) ToEnvVars() []corev1.EnvVar { + envVars := make([]corev1.EnvVar, 0, len(f.m)) + for featureFlag, val := range f.m { + envVars = append(envVars, corev1.EnvVar{ + Name: featureFlag, + Value: strconv.FormatBool(val), + }) + } + return envVars +} + +func (f FeatureFlags) set(featureFlag string, val bool) error { + if !strings.HasPrefix(featureFlag, "ROX_") { + return fmt.Errorf("invalid feature flag name: %s", featureFlag) + } + f.m[featureFlag] = val + return nil +} + +func (f FeatureFlags) parseAndSetOne(setting string) error { + featureFlag, valStr, found := strings.Cut(setting, "=") + if found { + val, err := strconv.ParseBool(valStr) + if err != nil { + return fmt.Errorf("invalid feature flag value: %s", valStr) + } + return f.set(featureFlag, val) + } + + // Look at first character to determine boolean value. + val := true + switch featureFlag[0] { + case '+': + featureFlag = featureFlag[1:] + case '-': + featureFlag = featureFlag[1:] + val = false + } + return f.set(featureFlag, val) +} + +func (f FeatureFlags) ParseAndSetFromSlice(settingsSlice []string) error { + for _, settingsString := range settingsSlice { + settings := strings.Split(settingsString, ",") + if err := f.parseAndSetFromString(settingsString); err != nil { + return err + } + for _, setting := range settings { + setting = strings.TrimSpace(setting) + if err := f.parseAndSetOne(setting); err != nil { + return err + } + } + } + return nil +} + +func (f FeatureFlags) parseAndSetFromString(settingsString string) error { + settings := strings.Split(settingsString, ",") + for _, setting := range settings { + setting = strings.TrimSpace(setting) + if err := f.parseAndSetOne(setting); err != nil { + return err + } + } + return nil +} diff --git a/internal/deployer/feature_flags_test.go b/internal/deployer/feature_flags_test.go new file mode 100644 index 00000000..7c968e66 --- /dev/null +++ b/internal/deployer/feature_flags_test.go @@ -0,0 +1,274 @@ +package deployer + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" +) + +func TestFeatureFlags_parseAndSetOne(t *testing.T) { + tests := []struct { + name string + setting string + expectedKey string + expectedVal bool + expectedError bool + }{ + { + name: "explicit true with equals", + setting: "ROX_FEATURE_1=true", + expectedKey: "ROX_FEATURE_1", + expectedVal: true, + }, + { + name: "explicit false with equals", + setting: "ROX_FEATURE_2=false", + expectedKey: "ROX_FEATURE_2", + expectedVal: false, + }, + { + name: "explicit 1 (true)", + setting: "ROX_FEATURE_3=1", + expectedKey: "ROX_FEATURE_3", + expectedVal: true, + }, + { + name: "explicit 0 (false)", + setting: "ROX_FEATURE_4=0", + expectedKey: "ROX_FEATURE_4", + expectedVal: false, + }, + { + name: "plus prefix (enabled)", + setting: "+ROX_FEATURE_5", + expectedKey: "ROX_FEATURE_5", + expectedVal: true, + }, + { + name: "minus prefix (disabled)", + setting: "-ROX_FEATURE_6", + expectedKey: "ROX_FEATURE_6", + expectedVal: false, + }, + { + name: "no prefix (defaults to true)", + setting: "ROX_FEATURE_7", + expectedKey: "ROX_FEATURE_7", + expectedVal: true, + }, + { + name: "invalid value", + setting: "ROX_FEATURE_8=invalid", + expectedError: true, + }, + { + name: "invalid prefix", + setting: "FEATURE_9=true", + expectedError: true, + }, + { + name: "invalid prefix with plus", + setting: "+FEATURE_10", + expectedError: true, + }, + { + name: "invalid prefix with minus", + setting: "-FEATURE_11", + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ff := NewFeatureFlags() + err := ff.parseAndSetOne(tt.setting) + if tt.expectedError { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expectedVal, ff.m[tt.expectedKey]) + } + }) + } +} + +func TestFeatureFlags_parseAndSetFromString(t *testing.T) { + tests := []struct { + name string + input string + expected map[string]bool + expectError bool + }{ + { + name: "single flag", + input: "ROX_FEATURE_1=true", + expected: map[string]bool{ + "ROX_FEATURE_1": true, + }, + expectError: false, + }, + { + name: "multiple flags comma-separated", + input: "ROX_FEATURE_1=true,ROX_FEATURE_2=false,+ROX_FEATURE_3,-ROX_FEATURE_4", + expected: map[string]bool{ + "ROX_FEATURE_1": true, + "ROX_FEATURE_2": false, + "ROX_FEATURE_3": true, + "ROX_FEATURE_4": false, + }, + expectError: false, + }, + { + name: "flags with spaces", + input: "ROX_FEATURE_1=true, ROX_FEATURE_2=false , +ROX_FEATURE_3 , -ROX_FEATURE_4", + expected: map[string]bool{ + "ROX_FEATURE_1": true, + "ROX_FEATURE_2": false, + "ROX_FEATURE_3": true, + "ROX_FEATURE_4": false, + }, + expectError: false, + }, + { + name: "mixed formats", + input: "ROX_FEATURE_1,+ROX_FEATURE_2,-ROX_FEATURE_3,ROX_FEATURE_4=1,ROX_FEATURE_5=0", + expected: map[string]bool{ + "ROX_FEATURE_1": true, + "ROX_FEATURE_2": true, + "ROX_FEATURE_3": false, + "ROX_FEATURE_4": true, + "ROX_FEATURE_5": false, + }, + expectError: false, + }, + { + name: "invalid flag in list", + input: "ROX_FEATURE_1=true,INVALID_FLAG=true", + expectError: true, + }, + { + name: "invalid value in list", + input: "ROX_FEATURE_1=true,ROX_FEATURE_2=invalid", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ff := NewFeatureFlags() + err := ff.parseAndSetFromString(tt.input) + if tt.expectError { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expected, ff.m) + } + }) + } +} + +func TestFeatureFlags_ParseAndSetFromSlice(t *testing.T) { + tests := []struct { + name string + input []string + expected map[string]bool + expectError bool + }{ + { + name: "single string with single flag", + input: []string{"ROX_FEATURE_1=true"}, + expected: map[string]bool{ + "ROX_FEATURE_1": true, + }, + }, + { + name: "single string with multiple flags", + input: []string{"ROX_FEATURE_1=true,ROX_FEATURE_2=false"}, + expected: map[string]bool{ + "ROX_FEATURE_1": true, + "ROX_FEATURE_2": false, + }, + }, + { + name: "multiple strings", + input: []string{ + "ROX_FEATURE_1=true,ROX_FEATURE_2=false", + "+ROX_FEATURE_3,-ROX_FEATURE_4", + }, + expected: map[string]bool{ + "ROX_FEATURE_1": true, + "ROX_FEATURE_2": false, + "ROX_FEATURE_3": true, + "ROX_FEATURE_4": false, + }, + }, + { + name: "override previous value", + input: []string{ + "ROX_FEATURE_1=true", + "ROX_FEATURE_1=false", + }, + expected: map[string]bool{ + "ROX_FEATURE_1": false, + }, + }, + { + name: "empty slice", + input: []string{}, + expected: map[string]bool{}, + }, + { + name: "error in first string", + input: []string{ + "INVALID_FLAG=true", + "ROX_FEATURE_1=true", + }, + expectError: true, + }, + { + name: "error in second string", + input: []string{ + "ROX_FEATURE_1=true", + "INVALID_FLAG=true", + }, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ff := NewFeatureFlags() + err := ff.ParseAndSetFromSlice(tt.input) + if tt.expectError { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expected, ff.m) + } + }) + } +} + +func TestFeatureFlags_Integration(t *testing.T) { + t.Run("parse from slice and convert to env vars", func(t *testing.T) { + ff := NewFeatureFlags() + err := ff.ParseAndSetFromSlice([]string{ + "ROX_FEATURE_1=true,ROX_FEATURE_2=false", + "+ROX_FEATURE_3,-ROX_FEATURE_4", + "ROX_FEATURE_5=1", + }) + require.NoError(t, err) + + envVars := ff.ToEnvVars() + expected := []corev1.EnvVar{ + {Name: "ROX_FEATURE_1", Value: "true"}, + {Name: "ROX_FEATURE_2", Value: "false"}, + {Name: "ROX_FEATURE_3", Value: "true"}, + {Name: "ROX_FEATURE_4", Value: "false"}, + {Name: "ROX_FEATURE_5", Value: "true"}, + } + assert.ElementsMatch(t, expected, envVars) + }) +} From 7837159946c0b546604e9b1a0e8ea47e10e96726 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 17 Dec 2025 23:01:27 +0100 Subject: [PATCH 2/4] Integrate into deployer --- cmd/deploy.go | 8 ++++++++ cmd/main.go | 1 + internal/deployer/deploy_via_operator.go | 7 +++++++ internal/deployer/deployer.go | 10 ++++++++++ 4 files changed, 26 insertions(+) diff --git a/cmd/deploy.go b/cmd/deploy.go index 2b4ecc50..e7c48f61 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -42,6 +42,8 @@ Examples: cmd.Flags().StringVar(&shell, "shell", "", "Shell to spawn after Central deployment") cmd.Flags().StringVar(&envrc, "envrc", "", "Write environment to file instead of spawning sub-shell") cmd.Flags().BoolVar(&singleNamespace, "single-namespace", false, "Deploy all components in a single namespace ('stackrox' by default)") + cmd.Flags().StringArrayVar(&featureFlagsSlice, "features", []string{}, "Feature flag settings") + cmd.Flags().StringArrayVar(&featureFlagsSlice, "feature", []string{}, "(alias for --features)") return cmd } @@ -145,6 +147,12 @@ func runDeploy(cmd *cobra.Command, args []string) error { } d.SetMainImageTag(mainImageTag) + featureFlags := deployer.NewFeatureFlags() + if err := featureFlags.ParseAndSetFromSlice(featureFlagsSlice); err != nil { + return fmt.Errorf("failed to parse feature flags: %w", err) + } + d.SetFeatureFlags(featureFlags) + // Resolve "auto" resources based on cluster type resolvedResources := resources if resources == "auto" { diff --git a/cmd/main.go b/cmd/main.go index 40e0e822..70a52ece 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,6 +23,7 @@ var ( shell string envrc string singleNamespace bool + featureFlagsSlice []string ) func main() { diff --git a/internal/deployer/deploy_via_operator.go b/internal/deployer/deploy_via_operator.go index ab48c8cd..1ef78de1 100644 --- a/internal/deployer/deploy_via_operator.go +++ b/internal/deployer/deploy_via_operator.go @@ -224,6 +224,10 @@ func (d *Deployer) createAdminPasswordSecret(ctx context.Context) error { // createCentralCR creates the Central custom resource func (d *Deployer) createCentralCR(resources, exposure string) (map[string]interface{}, error) { + featureFlags := d.featureFlags.ToEnvVars() + for _, featureFlag := range featureFlags { + d.logger.Dimf("Setting Feature flag: %s=%s", featureFlag.Name, featureFlag.Value) + } base := map[string]interface{}{ "apiVersion": "platform.stackrox.io/v1alpha1", "kind": "Central", @@ -266,6 +270,9 @@ func (d *Deployer) createCentralCR(resources, exposure string) (map[string]inter }, }, }, + "customize": map[string]interface{}{ + "envVars": featureFlags, + }, }, } diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 0fb8e9d8..a271831d 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -117,6 +117,7 @@ type Deployer struct { earlyReadiness bool dockerCreds *dockerauth.Credentials clusterResourceKinds map[string]struct{} + featureFlags FeatureFlags } type ResourceKindWithName struct { @@ -709,6 +710,7 @@ func (d *Deployer) SetPauseReconciliation(enabled bool) { d.pauseReconciliation = enabled } +<<<<<<< HEAD func (d *Deployer) SetSingleNamespace(enabled bool) { if enabled { d.centralNamespace = sharedNamespace @@ -721,6 +723,14 @@ func (d *Deployer) SetMainImageTag(tag string) { d.operatorTag = helpers.ConvertMainTagToOperatorTag(d.mainImageTag) } +||||||| parent of 78da95e (Integrate into deployer) +======= +func (d *Deployer) SetFeatureFlags(featureFlags FeatureFlags) error { + d.featureFlags = featureFlags + return nil +} + +>>>>>>> 78da95e (Integrate into deployer) // maybeAddPauseReconcileAnnotation adds the stackrox.io/pause-reconcile annotation to a custom resource func (d *Deployer) maybeAddPauseReconcileAnnotation(ctx context.Context, resourceType, resourceName, namespace string) error { if !d.pauseReconciliation { From 17ea67229ecd55da33bb1d3da4b6ce45f6dd5922 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 17 Dec 2025 23:02:12 +0100 Subject: [PATCH 3/4] go.mod & go.sum --- go.mod | 28 +++++++++++++++-- go.sum | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 91f382c0..48f793d0 100644 --- a/go.mod +++ b/go.mod @@ -2,17 +2,41 @@ module github.com/stackrox/roxie go 1.25 +toolchain go1.25.5 + require ( github.com/fatih/color v1.16.0 github.com/moby/sys/mountinfo v0.7.2 github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.10.0 gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.34.3 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.14.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/apimachinery v0.34.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) diff --git a/go.sum b/go.sum index 651f17ee..f898917c 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,29 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -10,16 +31,85 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From 3fdf3cfd1ca89fab363f60908f5c58ed5bb80b7a Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 17 Dec 2025 23:03:12 +0100 Subject: [PATCH 4/4] sc --- internal/deployer/deploy_via_operator.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/deployer/deploy_via_operator.go b/internal/deployer/deploy_via_operator.go index 1ef78de1..066f98f9 100644 --- a/internal/deployer/deploy_via_operator.go +++ b/internal/deployer/deploy_via_operator.go @@ -617,6 +617,10 @@ func (d *Deployer) deploySecuredClusterOperator(ctx context.Context, resources s // createSecuredClusterCR creates the SecuredCluster custom resource. func (d *Deployer) createSecuredClusterCR(clusterName, resources string) (map[string]interface{}, error) { + featureFlags := d.featureFlags.ToEnvVars() + for _, featureFlag := range featureFlags { + d.logger.Dimf("Setting Feature flag: %s=%s", featureFlag.Name, featureFlag.Value) + } base := map[string]interface{}{ "apiVersion": "platform.stackrox.io/v1alpha1", "kind": "SecuredCluster", @@ -652,6 +656,9 @@ func (d *Deployer) createSecuredClusterCR(clusterName, resources string) (map[st }, }, }, + "customize": map[string]interface{}{ + "envVars": featureFlags, + }, }, }