From d876f0da3595066d2c751c59f1fdb4a01dfd02ce Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Tue, 5 May 2026 10:58:59 +0200 Subject: [PATCH 1/2] Improve checking of availability of securityContextConfig --- internal/deployer/operator_olm.go | 72 ++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/internal/deployer/operator_olm.go b/internal/deployer/operator_olm.go index 662fbd0..cb30247 100644 --- a/internal/deployer/operator_olm.go +++ b/internal/deployer/operator_olm.go @@ -10,6 +10,7 @@ import ( "github.com/stackrox/roxie/internal/k8s" "gopkg.in/yaml.v3" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) const ( @@ -107,11 +108,21 @@ func (d *Deployer) getOperatorIndexImage() string { func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) error { d.logger.Info("Creating CatalogSource...") - // Check if CatalogSource CRD supports securityContextConfig (OCP 4.14+). - hasSecurityContextConfig, err := d.catalogSourceSupportsSecurityContextConfig(ctx) + securityContextConfigSupported, err := d.catalogSourceSupportsSecurityContextConfig(ctx) if err != nil { d.logger.Warning("Could not check CatalogSource CRD capabilities, proceeding without securityContextConfig") - hasSecurityContextConfig = false + securityContextConfigSupported = false + } + + catalogSourceSpec := map[string]interface{}{ + "sourceType": "grpc", + "image": indexImage, + "displayName": "StackRox Operator Index", + } + if securityContextConfigSupported { + catalogSourceSpec["grpcPodConfig"] = map[string]interface{}{ + "securityContextConfig": "restricted", + } } catalogSource := map[string]interface{}{ @@ -121,19 +132,7 @@ func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) e "name": catalogSourceName, "namespace": operatorNamespace, }, - "spec": map[string]interface{}{ - "sourceType": "grpc", - "image": indexImage, - "displayName": "StackRox Operator Index", - }, - } - - // TODO(ROX-34499): Add security context config if supported. - if hasSecurityContextConfig { - spec := catalogSource["spec"].(map[string]interface{}) - spec["grpcPodConfig"] = map[string]interface{}{ - "securityContextConfig": "restricted", - } + "spec": catalogSourceSpec, } yamlData, err := yaml.Marshal(catalogSource) @@ -153,18 +152,49 @@ func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) e return nil } -// catalogSourceSupportsSecurityContextConfig checks if the CatalogSource CRD supports securityContextConfig. +// catalogSourceSupportsSecurityContextConfig checks if any served CatalogSource CRD version +// includes securityContextConfig in its schema. func (d *Deployer) catalogSourceSupportsSecurityContextConfig(ctx context.Context) (bool, error) { + crdName := "catalogsources.operators.coreos.com" result, err := d.runKubectl(ctx, k8s.KubectlOptions{ - Args: []string{"get", "crd", "catalogsources.operators.coreos.com", "-o", "yaml"}, + Args: []string{"get", "crd", crdName, "-o", "yaml"}, }) if err != nil { return false, err } - // TODO(ROX-34499): this is overly optimistic and would incorrectly succeed if an api version - // that contains this had "serving: false" - return strings.Contains(result.Stdout, "securityContextConfig"), nil + obj := &unstructured.Unstructured{} + if err := yaml.Unmarshal([]byte(result.Stdout), &obj.Object); err != nil { + return false, fmt.Errorf("failed to parse CatalogSource CRD: %w", err) + } + + // Note, we cannot use NestedSlice, because that does an implicit runtime.DeepCopyJSONValue, which fail, + // because the versions slice YAML also contains unsupported types (int). + versions, _, _ := unstructured.NestedFieldNoCopy(obj.Object, "spec", "versions") + versionsSlice, ok := versions.([]interface{}) + if !ok { + return false, fmt.Errorf("failed to extract spec.versions from crd %s", crdName) + } + + for _, v := range versionsSlice { + version, ok := v.(map[string]interface{}) + if !ok { + continue + } + served, _, _ := unstructured.NestedBool(version, "served") + if !served { + continue + } + _, found, _ := unstructured.NestedMap(version, + "schema", "openAPIV3Schema", "properties", "spec", + "properties", "grpcPodConfig", "properties", "securityContextConfig", + ) + if found { + return true, nil + } + } + + return false, nil } // createOperatorGroup creates the OperatorGroup. From ce8e5329b4f3d5139d4d2df498ffc0be5ea4fee7 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Thu, 7 May 2026 15:16:50 +0200 Subject: [PATCH 2/2] Unconditionally include securityContextConfig and disable validation --- internal/deployer/operator_olm.go | 76 +++++-------------------------- 1 file changed, 11 insertions(+), 65 deletions(-) diff --git a/internal/deployer/operator_olm.go b/internal/deployer/operator_olm.go index cb30247..22c7b48 100644 --- a/internal/deployer/operator_olm.go +++ b/internal/deployer/operator_olm.go @@ -10,7 +10,6 @@ import ( "github.com/stackrox/roxie/internal/k8s" "gopkg.in/yaml.v3" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) const ( @@ -108,23 +107,6 @@ func (d *Deployer) getOperatorIndexImage() string { func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) error { d.logger.Info("Creating CatalogSource...") - securityContextConfigSupported, err := d.catalogSourceSupportsSecurityContextConfig(ctx) - if err != nil { - d.logger.Warning("Could not check CatalogSource CRD capabilities, proceeding without securityContextConfig") - securityContextConfigSupported = false - } - - catalogSourceSpec := map[string]interface{}{ - "sourceType": "grpc", - "image": indexImage, - "displayName": "StackRox Operator Index", - } - if securityContextConfigSupported { - catalogSourceSpec["grpcPodConfig"] = map[string]interface{}{ - "securityContextConfig": "restricted", - } - } - catalogSource := map[string]interface{}{ "apiVersion": "operators.coreos.com/v1alpha1", "kind": "CatalogSource", @@ -132,7 +114,14 @@ func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) e "name": catalogSourceName, "namespace": operatorNamespace, }, - "spec": catalogSourceSpec, + "spec": map[string]interface{}{ + "sourceType": "grpc", + "image": indexImage, + "displayName": "StackRox Operator Index", + "grpcPodConfig": map[string]interface{}{ + "securityContextConfig": "restricted", + }, + }, } yamlData, err := yaml.Marshal(catalogSource) @@ -141,7 +130,9 @@ func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) e } _, err = d.runKubectl(ctx, k8s.KubectlOptions{ - Args: []string{"apply", "-f", "-"}, + // Apply with --validate=ignore because securityContextConfig may not + // be in the CatalogSource CRD schema. + Args: []string{"apply", "--validate=ignore", "-f", "-"}, Stdin: bytes.NewReader(yamlData), }) if err != nil { @@ -152,51 +143,6 @@ func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) e return nil } -// catalogSourceSupportsSecurityContextConfig checks if any served CatalogSource CRD version -// includes securityContextConfig in its schema. -func (d *Deployer) catalogSourceSupportsSecurityContextConfig(ctx context.Context) (bool, error) { - crdName := "catalogsources.operators.coreos.com" - result, err := d.runKubectl(ctx, k8s.KubectlOptions{ - Args: []string{"get", "crd", crdName, "-o", "yaml"}, - }) - if err != nil { - return false, err - } - - obj := &unstructured.Unstructured{} - if err := yaml.Unmarshal([]byte(result.Stdout), &obj.Object); err != nil { - return false, fmt.Errorf("failed to parse CatalogSource CRD: %w", err) - } - - // Note, we cannot use NestedSlice, because that does an implicit runtime.DeepCopyJSONValue, which fail, - // because the versions slice YAML also contains unsupported types (int). - versions, _, _ := unstructured.NestedFieldNoCopy(obj.Object, "spec", "versions") - versionsSlice, ok := versions.([]interface{}) - if !ok { - return false, fmt.Errorf("failed to extract spec.versions from crd %s", crdName) - } - - for _, v := range versionsSlice { - version, ok := v.(map[string]interface{}) - if !ok { - continue - } - served, _, _ := unstructured.NestedBool(version, "served") - if !served { - continue - } - _, found, _ := unstructured.NestedMap(version, - "schema", "openAPIV3Schema", "properties", "spec", - "properties", "grpcPodConfig", "properties", "securityContextConfig", - ) - if found { - return true, nil - } - } - - return false, nil -} - // createOperatorGroup creates the OperatorGroup. func (d *Deployer) createOperatorGroup(ctx context.Context) error { d.logger.Info("Creating OperatorGroup...")