Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,16 @@
"lifecycle": "blocking",
"environmentSelector": {}
},
{
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {clusterextensionrevisions/finalizer} is not specified",
"labels": {},
"resources": {
"isolation": {}
},
"source": "openshift:payload:olmv1",
"lifecycle": "blocking",
"environmentSelector": {}
},
{
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {escalate, bind} is not specified",
"labels": {},
Expand Down
4 changes: 2 additions & 2 deletions openshift/tests-extension/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ require (
github.com/onsi/ginkgo/v2 v2.27.2
github.com/onsi/gomega v1.38.2
github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7
github.com/openshift/api v0.0.0-20251106190826-ebe535b08719
github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235
github.com/openshift/api v0.0.0-20260128000234-c16ec2bcf089
github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13
github.com/openshift/origin v1.5.0-alpha.3.0.20251010041851-79ff1dbbe815
github.com/operator-framework/operator-controller v1.5.1
github.com/pborman/uuid v1.2.1
Expand Down
8 changes: 4 additions & 4 deletions openshift/tests-extension/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7 h1:Z1swlS6b3Adm6RPhjqefs3DWnNFLDxRX+WC8GMXhja4=
github.com/openshift-eng/openshift-tests-extension v0.0.0-20251105193959-75a0be5d9bd7/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M=
github.com/openshift/api v0.0.0-20251106190826-ebe535b08719 h1:KEwYyKaJniwhoyLB75tAMmJn9pMlk0PUlRfrsXYOhwM=
github.com/openshift/api v0.0.0-20251106190826-ebe535b08719/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY=
github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235 h1:9JBeIXmnHlpXTQPi7LPmu1jdxznBhAE7bb1K+3D8gxY=
github.com/openshift/client-go v0.0.0-20251015124057-db0dee36e235/go.mod h1:L49W6pfrZkfOE5iC1PqEkuLkXG4W0BX4w8b+L2Bv7fM=
github.com/openshift/api v0.0.0-20260128000234-c16ec2bcf089 h1:qcKLN7H1dh2wt59Knpc1J5XzCCStSeaaFyEHHilFypg=
github.com/openshift/api v0.0.0-20260128000234-c16ec2bcf089/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY=
github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13 h1:6rd4zSo2UaWQcAPZfHK9yzKVqH0BnMv1hqMzqXZyTds=
github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13/go.mod h1:YvOmPmV7wcJxpfhTDuFqqs2Xpb3M3ovsM6Qs/i2ptq4=
github.com/openshift/kubernetes v0.0.0-20251108023427-891f5bb03061 h1:XVgudZfcjtF8UPIUarXXu6z7tZJLxrenIXOaB8e0tRk=
github.com/openshift/kubernetes v0.0.0-20251108023427-891f5bb03061/go.mod h1:w3+IfrXNp5RosdDXg3LB55yijJqR/FwouvVntYHQf0o=
github.com/openshift/kubernetes/staging/src/k8s.io/api v0.0.0-20251108023427-891f5bb03061 h1:uE4i/OdgU+YypcJ7vc8abZJQRyd6zwnUpY9nSdBAHEs=
Expand Down
12 changes: 6 additions & 6 deletions openshift/tests-extension/pkg/bindata/operator/operator.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 68 additions & 6 deletions openshift/tests-extension/pkg/helpers/feature_capability.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package helpers

import (
"context"
"fmt"

//nolint:staticcheck // ST1001: dot-imports for readability
. "github.com/onsi/ginkgo/v2"
Expand All @@ -23,18 +24,79 @@ func RequireOLMv1CapabilityOnOpenshift() {
extlogs.Warn("Skipping feature capability check: not OpenShift")
return
}

ctx := context.TODO()
clientset, err := configclient.NewForConfig(env.Get().RestCfg)
Expect(err).NotTo(HaveOccurred(), "failed to create config client")

clientset := configv1Client()
cv, err := clientset.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred(), "failed to retrieve cluster version")

for _, cap := range cv.Status.Capabilities.EnabledCapabilities {
if cap == configv1.ClusterVersionCapabilityOperatorLifecycleManagerV1 {
for _, capability := range cv.Status.Capabilities.EnabledCapabilities {
if capability == configv1.ClusterVersionCapabilityOperatorLifecycleManagerV1 {
return
}
}
Skip("Test requires OperatorLifecycleManagerV1 capability")
}

// RequireFeatureGateEnabled requires featureGate to be in the list of enabled feature gates
// for the OCP version in env.Get().OpenShiftVersion in the status of the
// featuregates.config.openshift.io cluster resource
func RequireFeatureGateEnabled(featureGate configv1.FeatureGateName) {
if !IsFeatureGateEnabled(featureGate) {
Skip(fmt.Sprintf("Test requires %q feature gate to be enabled", featureGate))
}
}

// RequireFeatureGateDisabled requires featureGate to be in the list of disabled feature gates
// for the OCP version in env.Get().OpenShiftVersion in the status of the
// featuregates.config.openshift.io cluster resource
func RequireFeatureGateDisabled(featureGate configv1.FeatureGateName) {
if IsFeatureGateEnabled(featureGate) {
Skip(fmt.Sprintf("Test requires %q feature gate to be disabled", featureGate))
}
}

func IsFeatureGateEnabled(featureGate configv1.FeatureGateName) bool {
featureGates := getFeatureGateDetails(configv1Client())
for _, fg := range featureGates.Enabled {
if fg.Name == featureGate {
return true
}
}
return false
}

func getFeatureGateDetails(clientset *configclient.Clientset) configv1.FeatureGateDetails {
ctx := context.TODO()
featureGates, err := clientset.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred(), "failed to retrieve feature gates")

openshiftVersion := getOpenShiftVersion(clientset)
for _, fgs := range featureGates.Status.FeatureGates {
if fgs.Version == openshiftVersion {
return fgs
}
}
Fail(fmt.Sprintf("No feature gates found for version %s", openshiftVersion))
return configv1.FeatureGateDetails{}
}

func getOpenShiftVersion(clientset *configclient.Clientset) string {
// Fetch the single global 'version' resource
cv, err := clientset.ConfigV1().ClusterVersions().Get(context.TODO(), "version", metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred(), "failed to retrieve cluster version")

// The status.history is a slice of updates.
// The first element (index 0) is the current or most recent version.
if len(cv.Status.History) > 0 {
return cv.Status.History[0].Version
}

Fail("No history found in cluster version status")
return ""
}

func configv1Client() *configclient.Clientset {
clientset, err := configclient.NewForConfig(env.Get().RestCfg)
Expect(err).NotTo(HaveOccurred(), "failed to create config client")
return clientset
}
82 changes: 56 additions & 26 deletions openshift/tests-extension/test/olmv1-preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
//nolint:staticcheck // ST1001: dot-imports for readability
. "github.com/onsi/gomega"

"github.com/openshift/api/features"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/meta"
Expand All @@ -22,6 +23,18 @@ import (
"github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/helpers"
)

type preflightAuthTestScenario int

const (
scenarioMissingServicePerms preflightAuthTestScenario = 0
scenarioMissingCreateVerb preflightAuthTestScenario = 1
scenarioMissingClusterRoleBindingsPerms preflightAuthTestScenario = 2
scenarioMissingNamedConfigMapPerms preflightAuthTestScenario = 3
scenarioMissingClusterExtensionsFinalizerPerms preflightAuthTestScenario = 4
scenarioMissingEscalateAndBindPerms preflightAuthTestScenario = 5
scenarioMissingClusterExtensionRevisionsFinalizerPerms preflightAuthTestScenario = 6
)

var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks", func() {
var (
namespace string
Expand All @@ -45,33 +58,39 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Sk
})

It("should report error when {services} are not specified", func(ctx SpecContext) {
runNegativePreflightTest(ctx, 0, namespace)
runNegativePreflightTest(ctx, scenarioMissingServicePerms, namespace)
})

It("should report error when {create} verb is not specified", func(ctx SpecContext) {
runNegativePreflightTest(ctx, 1, namespace)
runNegativePreflightTest(ctx, scenarioMissingCreateVerb, namespace)
})

It("should report error when {ClusterRoleBindings} are not specified", func(ctx SpecContext) {
runNegativePreflightTest(ctx, 2, namespace)
runNegativePreflightTest(ctx, scenarioMissingClusterRoleBindingsPerms, namespace)
})

It("should report error when {ConfigMap:resourceNames} are not all specified", func(ctx SpecContext) {
runNegativePreflightTest(ctx, 3, namespace)
runNegativePreflightTest(ctx, scenarioMissingNamedConfigMapPerms, namespace)
})

It("should report error when {clusterextension/finalizer} is not specified", func(ctx SpecContext) {
runNegativePreflightTest(ctx, 4, namespace)
helpers.RequireFeatureGateDisabled(features.FeatureGateNewOLMBoxCutterRuntime)
runNegativePreflightTest(ctx, scenarioMissingClusterExtensionsFinalizerPerms, namespace)
})

It("should report error when {clusterextensionrevisions/finalizer} is not specified", func(ctx SpecContext) {
helpers.RequireFeatureGateEnabled(features.FeatureGateNewOLMBoxCutterRuntime)
runNegativePreflightTest(ctx, scenarioMissingClusterExtensionRevisionsFinalizerPerms, namespace)
})

It("should report error when {escalate, bind} is not specified", func(ctx SpecContext) {
runNegativePreflightTest(ctx, 5, namespace)
runNegativePreflightTest(ctx, scenarioMissingEscalateAndBindPerms, namespace)
})
})

// runNegativePreflightTest creates a deficient ClusterRole and a ClusterExtension that
// relies on it, then waits for the expected preflight failure.
func runNegativePreflightTest(ctx context.Context, scenario int, namespace string) {
func runNegativePreflightTest(ctx context.Context, scenario preflightAuthTestScenario, namespace string) {
k8sClient := env.Get().K8sClient
unique := rand.String(8)

Expand Down Expand Up @@ -120,22 +139,33 @@ func runNegativePreflightTest(ctx context.Context, scenario int, namespace strin
g.Expect(c).NotTo(BeNil())
g.Expect(c.Status).To(Equal(metav1.ConditionTrue))
g.Expect(c.Message).To(ContainSubstring("pre-authorization failed"))

c = meta.FindStatusCondition(latest.Status.Conditions, "Installed")
g.Expect(c).NotTo(BeNil())
g.Expect(c.Status).To(Equal(metav1.ConditionFalse))
}).WithTimeout(helpers.DefaultTimeout).WithPolling(helpers.DefaultPolling).Should(Succeed())
}

// createDeficientClusterRole returns a modified ClusterRole according to the test scenario.
func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.ClusterRole {
baseRules := []rbacv1.PolicyRule{
{
APIGroups: []string{"olm.operatorframework.io"},
Resources: []string{"clusterextensions/finalizers"},
Verbs: []string{"update"},
ResourceNames: []string{ceName},
},
func createDeficientClusterRole(scenario preflightAuthTestScenario, name, ceName string) *rbacv1.ClusterRole {
var baseRules []rbacv1.PolicyRule
if helpers.IsFeatureGateEnabled(features.FeatureGateNewOLMBoxCutterRuntime) {
baseRules = []rbacv1.PolicyRule{
{
APIGroups: []string{"olm.operatorframework.io"},
Resources: []string{"clusterextensionrevisions/finalizers"},
Verbs: []string{"update"},
ResourceNames: []string{ceName},
},
}
} else {
baseRules = []rbacv1.PolicyRule{
{
APIGroups: []string{"olm.operatorframework.io"},
Resources: []string{"clusterextensions/finalizers"},
Verbs: []string{"update"},
ResourceNames: []string{ceName},
},
}
}

baseRules = append(baseRules, []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"nodes"},
Expand All @@ -151,14 +181,14 @@ func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.Clust
Resources: []string{"clusterroles", "clusterroles/finalizers", "roles", "roles/finalizers", "clusterrolebindings", "clusterrolebindings/finalizers", "rolebindings", "rolebindings/finalizers"},
Verbs: []string{"delete", "deletecollection", "create", "patch", "get", "list", "update", "watch", "bind", "escalate"},
},
}
}...)

// Copy rules to avoid mutation
rules := make([]rbacv1.PolicyRule, len(baseRules))
copy(rules, baseRules)

switch scenario {
case 0:
case scenarioMissingServicePerms:
// Remove 'services' and 'services/finalizers'
for i, r := range rules {
if r.APIGroups[0] == "" {
Expand All @@ -171,7 +201,7 @@ func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.Clust
rules[i].Resources = filtered
}
}
case 1:
case scenarioMissingCreateVerb:
// Remove 'create' verb
for i, r := range rules {
if r.APIGroups[0] == "" {
Expand All @@ -184,7 +214,7 @@ func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.Clust
rules[i].Verbs = filtered
}
}
case 2:
case scenarioMissingClusterRoleBindingsPerms:
// Remove 'clusterrolebindings'
for i, r := range rules {
if r.APIGroups[0] == "rbac.authorization.k8s.io" {
Expand All @@ -197,7 +227,7 @@ func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.Clust
rules[i].Resources = filtered
}
}
case 3:
case scenarioMissingNamedConfigMapPerms:
// Restrict configmaps to named subset (resourceNames)
for i, r := range rules {
if r.APIGroups[0] == "" {
Expand All @@ -216,7 +246,7 @@ func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.Clust
})
}
}
case 4:
case scenarioMissingClusterExtensionsFinalizerPerms:
// Remove olm.operatorframework.io permission for finalizers
filtered := []rbacv1.PolicyRule{}
for _, r := range rules {
Expand All @@ -225,7 +255,7 @@ func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.Clust
}
}
rules = filtered
case 5:
case scenarioMissingEscalateAndBindPerms:
// Remove 'bind' and 'escalate' verbs
for i, r := range rules {
if r.APIGroups[0] == "rbac.authorization.k8s.io" {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading