From 215a72ca0572a62fc32b6e42ebc0e146104e9074 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Tue, 26 May 2026 15:43:40 -0400 Subject: [PATCH] UPSTREAM: : add OLMv1 topology-based deployment scaling e2e test Adds a new test that verifies cluster-olm-operator correctly configures operator-controller and catalogd deployments based on the cluster's control plane topology: - HA topologies (HighlyAvailable, HighlyAvailableArbiter, DualReplica): replicas=2 with a PodDisruptionBudget present - Non-HA topologies (SingleReplica/SNO, External): replicas=1, no PDB Also registers policyv1 in the test scheme to support PDB list queries. Assisted-by: claude Signed-off-by: Todd Short --- .../openshift_payload_olmv1.json | 10 ++ .../pkg/bindata/catalog/catalog.go | 6 +- .../pkg/bindata/operator/operator.go | 12 +- .../pkg/bindata/singleown/bundle/bundle.go | 20 ++-- .../pkg/bindata/singleown/index/index.go | 4 +- .../pkg/bindata/webhook/bundle/bundle.go | 22 ++-- .../pkg/bindata/webhook/index/index.go | 4 +- openshift/tests-extension/pkg/env/cluster.go | 2 + .../tests-extension/test/olmv1-topology.go | 107 ++++++++++++++++++ 9 files changed, 153 insertions(+), 34 deletions(-) create mode 100644 openshift/tests-extension/test/olmv1-topology.go diff --git a/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json b/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json index 6da088d542..6229899069 100644 --- a/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json +++ b/openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json @@ -1632,6 +1632,16 @@ "lifecycle": "blocking", "environmentSelector": {} }, + { + "name": "[sig-olmv1][OCPFeatureGate:NewOLM] OLMv1 topology-based deployment scaling should configure replicas and PodDisruptionBudgets to match the cluster control plane topology", + "labels": {}, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:olmv1", + "lifecycle": "blocking", + "environmentSelector": {} + }, { "name": "[sig-olmv1] OLMv1 should pass a trivial sanity check", "labels": {}, diff --git a/openshift/tests-extension/pkg/bindata/catalog/catalog.go b/openshift/tests-extension/pkg/bindata/catalog/catalog.go index b83d5fc0b2..7027b31f63 100644 --- a/openshift/tests-extension/pkg/bindata/catalog/catalog.go +++ b/openshift/tests-extension/pkg/bindata/catalog/catalog.go @@ -94,7 +94,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1760017176, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -114,7 +114,7 @@ func configsIndexignore() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "configs/.indexignore", size: 4, mode: os.FileMode(420), modTime: time.Unix(1760017176, 0)} + info := bindataFileInfo{name: "configs/.indexignore", size: 4, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -134,7 +134,7 @@ func configsIndexYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "configs/index.yaml", size: 669, mode: os.FileMode(420), modTime: time.Unix(1760123493, 0)} + info := bindataFileInfo{name: "configs/index.yaml", size: 669, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/pkg/bindata/operator/operator.go b/openshift/tests-extension/pkg/bindata/operator/operator.go index e51f2958f5..0178ed3aeb 100644 --- a/openshift/tests-extension/pkg/bindata/operator/operator.go +++ b/openshift/tests-extension/pkg/bindata/operator/operator.go @@ -97,7 +97,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1772823294, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -117,7 +117,7 @@ func manifestsRegistryClusterserviceversionYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/registry.clusterserviceversion.yaml", size: 4481, mode: os.FileMode(420), modTime: time.Unix(1772823294, 0)} + info := bindataFileInfo{name: "manifests/registry.clusterserviceversion.yaml", size: 4481, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -137,7 +137,7 @@ func manifestsScriptConfigmapYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/script.configmap.yaml", size: 300, mode: os.FileMode(420), modTime: time.Unix(1772823456, 0)} + info := bindataFileInfo{name: "manifests/script.configmap.yaml", size: 300, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -157,7 +157,7 @@ func metadataAnnotationsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "metadata/annotations.yaml", size: 732, mode: os.FileMode(420), modTime: time.Unix(1772823294, 0)} + info := bindataFileInfo{name: "metadata/annotations.yaml", size: 732, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -177,7 +177,7 @@ func metadataPropertiesYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "metadata/properties.yaml", size: 73, mode: os.FileMode(420), modTime: time.Unix(1772823294, 0)} + info := bindataFileInfo{name: "metadata/properties.yaml", size: 73, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -197,7 +197,7 @@ func testsScorecardConfigYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1772823294, 0)} + info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/pkg/bindata/singleown/bundle/bundle.go b/openshift/tests-extension/pkg/bindata/singleown/bundle/bundle.go index be93db6901..b30fbc45c6 100644 --- a/openshift/tests-extension/pkg/bindata/singleown/bundle/bundle.go +++ b/openshift/tests-extension/pkg/bindata/singleown/bundle/bundle.go @@ -101,7 +101,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -121,7 +121,7 @@ func manifestsWebhookOperatorControllerManagerMetricsService_v1_serviceYaml() (* return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-controller-manager-metrics-service_v1_service.yaml", size: 469, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-controller-manager-metrics-service_v1_service.yaml", size: 469, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -141,7 +141,7 @@ func manifestsWebhookOperatorMetricsReader_rbacAuthorizationK8sIo_v1_clusterrole return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 191, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 191, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -161,7 +161,7 @@ func manifestsWebhookOperatorWebhooktestAdminRole_rbacAuthorizationK8sIo_v1_clus return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 440, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 440, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -181,7 +181,7 @@ func manifestsWebhookOperatorWebhooktestEditorRole_rbacAuthorizationK8sIo_v1_clu return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 503, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 503, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -201,7 +201,7 @@ func manifestsWebhookOperatorWebhooktestViewerRole_rbacAuthorizationK8sIo_v1_clu return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 460, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 460, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -221,7 +221,7 @@ func manifestsWebhookOperatorClusterserviceversionYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator.clusterserviceversion.yaml", size: 6743, mode: os.FileMode(420), modTime: time.Unix(1760582299, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator.clusterserviceversion.yaml", size: 6743, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -241,7 +241,7 @@ func manifestsWebhookOperatorsCoreosIo_webhooktestsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook.operators.coreos.io_webhooktests.yaml", size: 11986, mode: os.FileMode(420), modTime: time.Unix(1760519201, 0)} + info := bindataFileInfo{name: "manifests/webhook.operators.coreos.io_webhooktests.yaml", size: 11986, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -261,7 +261,7 @@ func metadataAnnotationsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "metadata/annotations.yaml", size: 742, mode: os.FileMode(420), modTime: time.Unix(1760581498, 0)} + info := bindataFileInfo{name: "metadata/annotations.yaml", size: 742, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -281,7 +281,7 @@ func testsScorecardConfigYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/pkg/bindata/singleown/index/index.go b/openshift/tests-extension/pkg/bindata/singleown/index/index.go index d67af84481..6086f18830 100644 --- a/openshift/tests-extension/pkg/bindata/singleown/index/index.go +++ b/openshift/tests-extension/pkg/bindata/singleown/index/index.go @@ -93,7 +93,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1760519124, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -113,7 +113,7 @@ func configsIndexYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "configs/index.yaml", size: 1134, mode: os.FileMode(420), modTime: time.Unix(1760581511, 0)} + info := bindataFileInfo{name: "configs/index.yaml", size: 1134, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go b/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go index 1acc74d13e..f79770b5f7 100644 --- a/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go +++ b/openshift/tests-extension/pkg/bindata/webhook/bundle/bundle.go @@ -102,7 +102,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 888, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -122,7 +122,7 @@ func manifestsWebhookOperatorControllerManagerMetricsService_v1_serviceYaml() (* return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-controller-manager-metrics-service_v1_service.yaml", size: 469, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-controller-manager-metrics-service_v1_service.yaml", size: 469, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -142,7 +142,7 @@ func manifestsWebhookOperatorMetricsReader_rbacAuthorizationK8sIo_v1_clusterrole return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 191, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 191, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -162,7 +162,7 @@ func manifestsWebhookOperatorWebhookService_v1_serviceYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhook-service_v1_service.yaml", size: 395, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhook-service_v1_service.yaml", size: 395, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -182,7 +182,7 @@ func manifestsWebhookOperatorWebhooktestAdminRole_rbacAuthorizationK8sIo_v1_clus return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 440, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-admin-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 440, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -202,7 +202,7 @@ func manifestsWebhookOperatorWebhooktestEditorRole_rbacAuthorizationK8sIo_v1_clu return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 503, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 503, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -222,7 +222,7 @@ func manifestsWebhookOperatorWebhooktestViewerRole_rbacAuthorizationK8sIo_v1_clu return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 460, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator-webhooktest-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml", size: 460, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -242,7 +242,7 @@ func manifestsWebhookOperatorClusterserviceversionYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook-operator.clusterserviceversion.yaml", size: 8694, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook-operator.clusterserviceversion.yaml", size: 8694, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -262,7 +262,7 @@ func manifestsWebhookOperatorsCoreosIo_webhooktestsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "manifests/webhook.operators.coreos.io_webhooktests.yaml", size: 12240, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "manifests/webhook.operators.coreos.io_webhooktests.yaml", size: 12240, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -282,7 +282,7 @@ func metadataAnnotationsYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "metadata/annotations.yaml", size: 740, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "metadata/annotations.yaml", size: 740, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -302,7 +302,7 @@ func testsScorecardConfigYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "tests/scorecard/config.yaml", size: 1614, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/pkg/bindata/webhook/index/index.go b/openshift/tests-extension/pkg/bindata/webhook/index/index.go index 7eff667c98..0c401eb0c7 100644 --- a/openshift/tests-extension/pkg/bindata/webhook/index/index.go +++ b/openshift/tests-extension/pkg/bindata/webhook/index/index.go @@ -93,7 +93,7 @@ func dockerfile() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "Dockerfile", size: 97, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -113,7 +113,7 @@ func configsIndexYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "configs/index.yaml", size: 1122, mode: os.FileMode(420), modTime: time.Unix(1760231456, 0)} + info := bindataFileInfo{name: "configs/index.yaml", size: 1122, mode: os.FileMode(420), modTime: time.Unix(1777315519, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openshift/tests-extension/pkg/env/cluster.go b/openshift/tests-extension/pkg/env/cluster.go index a7ec211898..3ea17b01dc 100644 --- a/openshift/tests-extension/pkg/env/cluster.go +++ b/openshift/tests-extension/pkg/env/cluster.go @@ -15,6 +15,7 @@ import ( appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" + policyv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" @@ -83,6 +84,7 @@ func initTestEnv() *TestEnv { utilruntime.Must(appsv1.AddToScheme(scheme)) utilruntime.Must(rbacv1.AddToScheme(scheme)) utilruntime.Must(batchv1.AddToScheme(scheme)) + utilruntime.Must(policyv1.AddToScheme(scheme)) utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) utilruntime.Must(admissionregistrationv1.AddToScheme(scheme)) utilruntime.Must(olmv1.AddToScheme(scheme)) diff --git a/openshift/tests-extension/test/olmv1-topology.go b/openshift/tests-extension/test/olmv1-topology.go new file mode 100644 index 0000000000..e60d20ee2f --- /dev/null +++ b/openshift/tests-extension/test/olmv1-topology.go @@ -0,0 +1,107 @@ +package test + +import ( + "fmt" + + //nolint:staticcheck // ST1001: dot-imports for readability + . "github.com/onsi/ginkgo/v2" + //nolint:staticcheck // ST1001: dot-imports for readability + . "github.com/onsi/gomega" + + configv1 "github.com/openshift/api/config/v1" + appsv1 "k8s.io/api/apps/v1" + policyv1 "k8s.io/api/policy/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/env" + "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/helpers" +) + +// olmv1Component groups a deployment and its namespace for topology checks. +type olmv1Component struct { + namespace string + deploymentName string +} + +// olmv1Components lists the OLMv1 operand deployments that cluster-olm-operator manages. +var olmv1Components = []olmv1Component{ + {"openshift-operator-controller", "operator-controller-controller-manager"}, + {"openshift-catalogd", "catalogd-controller-manager"}, +} + +var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLM] OLMv1 topology-based deployment scaling", func() { + BeforeEach(func() { + helpers.RequireOLMv1CapabilityOnOpenshift() + }) + + // This test verifies the cluster-olm-operator behaviour described in + // pkg/controller/helm.go: HighlyAvailable / HighlyAvailableArbiter / + // DualReplica topologies get replicas=2 and a PodDisruptionBudget, while + // SingleReplica (SNO) and External topologies keep the chart default of + // replicas=1 with no PDB. + It("should configure replicas and PodDisruptionBudgets to match the cluster control plane topology", func(ctx SpecContext) { + k8sClient := env.Get().K8sClient + + infra := &configv1.Infrastructure{} + Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "cluster"}, infra)).To(Succeed()) + + topology := infra.Status.ControlPlaneTopology + isHA := topology == configv1.HighlyAvailableTopologyMode || + topology == configv1.HighlyAvailableArbiterMode || + topology == configv1.DualReplicaTopologyMode + + By(fmt.Sprintf("detected control plane topology: %q (HA=%v)", topology, isHA)) + + for _, c := range olmv1Components { + By(fmt.Sprintf("checking deployment %s/%s", c.namespace, c.deploymentName)) + + dep := &appsv1.Deployment{} + Expect(k8sClient.Get(ctx, client.ObjectKey{ + Namespace: c.namespace, + Name: c.deploymentName, + }, dep)).To(Succeed(), "deployment %s/%s should exist", c.namespace, c.deploymentName) + + if isHA { + Expect(dep.Spec.Replicas).NotTo(BeNil(), + "topology %q: deployment %s should have replicas explicitly set", topology, c.deploymentName) + Expect(*dep.Spec.Replicas).To(BeNumerically("==", 2), + "topology %q: deployment %s should have 2 replicas", topology, c.deploymentName) + } else { + // Kubernetes treats nil replicas as 1; both nil and the explicit value 1 are acceptable. + if dep.Spec.Replicas != nil { + Expect(*dep.Spec.Replicas).To(BeNumerically("==", 1), + "topology %q: deployment %s should have 1 replica", topology, c.deploymentName) + } + } + + By(fmt.Sprintf("checking PodDisruptionBudgets for deployment %s/%s", c.namespace, c.deploymentName)) + + pdbList := &policyv1.PodDisruptionBudgetList{} + Expect(k8sClient.List(ctx, pdbList, client.InNamespace(c.namespace))).To(Succeed()) + + // Filter to PDBs whose selector actually targets this deployment's pods. + podLabels := labels.Set(dep.Spec.Template.Labels) + var matchingPDBs []policyv1.PodDisruptionBudget + for _, pdb := range pdbList.Items { + if pdb.Spec.Selector == nil { + continue + } + sel, err := metav1.LabelSelectorAsSelector(pdb.Spec.Selector) + Expect(err).NotTo(HaveOccurred(), "PDB %s has an invalid selector", pdb.Name) + if sel.Matches(podLabels) { + matchingPDBs = append(matchingPDBs, pdb) + } + } + + if isHA { + Expect(matchingPDBs).NotTo(BeEmpty(), + "topology %q: deployment %s should have a PodDisruptionBudget", topology, c.deploymentName) + } else { + Expect(matchingPDBs).To(BeEmpty(), + "topology %q: deployment %s should have no PodDisruptionBudgets", topology, c.deploymentName) + } + } + }) +})