@@ -10,15 +10,19 @@ import (
1010 . "github.com/onsi/gomega"
1111
1212 "github.com/openshift/api/features"
13+ "github.com/openshift/origin/test/extended/util/image"
1314 corev1 "k8s.io/api/core/v1"
1415 rbacv1 "k8s.io/api/rbac/v1"
16+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1517 "k8s.io/apimachinery/pkg/api/meta"
1618 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1719 "k8s.io/apimachinery/pkg/util/rand"
1820 "sigs.k8s.io/controller-runtime/pkg/client"
1921
2022 olmv1 "github.com/operator-framework/operator-controller/api/v1"
2123
24+ singleownbundle "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/bindata/singleown/bundle"
25+ singleownindex "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/bindata/singleown/index"
2226 "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/env"
2327 "github.com/openshift/operator-framework-operator-controller/openshift/tests-extension/pkg/helpers"
2428)
@@ -35,16 +39,41 @@ const (
3539 scenarioMissingClusterExtensionRevisionsFinalizerPerms preflightAuthTestScenario = 6
3640)
3741
42+ const preflightBundleVersion = "0.0.5"
43+
3844var _ = Describe ("[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks" , func () {
3945 var (
40- namespace string
41- k8sClient client.Client
46+ namespace string
47+ k8sClient client.Client
48+ catalogName string
49+ packageName string
4250 )
43- BeforeEach (func () {
51+ BeforeEach (func (ctx SpecContext ) {
4452 helpers .RequireOLMv1CapabilityOnOpenshift ()
53+ helpers .RequireImageRegistry (ctx )
4554 k8sClient = env .Get ().K8sClient
4655 namespace = "preflight-test-ns-" + rand .String (4 )
4756
57+ // Use an in-cluster catalog and bundle so tests do not depend on external indexes.
58+ crdSuffix := rand .String (4 )
59+ packageName = fmt .Sprintf ("preflight-operator-%s" , crdSuffix )
60+ crdName := fmt .Sprintf ("webhooktests-%s.webhook.operators.coreos.io" , crdSuffix )
61+ helpers .EnsureCleanupClusterExtension (context .Background (), packageName , crdName )
62+
63+ singleownImage := image .LocationFor ("quay.io/olmtest/webhook-operator:v0.0.5" )
64+ replacements := map [string ]string {
65+ "{{ TEST-BUNDLE }}" : "" ,
66+ "{{ NAMESPACE }}" : "" ,
67+ "{{ TEST-CONTROLLER }}" : singleownImage ,
68+ "{{ CRD-SUFFIX }}" : crdSuffix ,
69+ "{{ PACKAGE-NAME }}" : packageName ,
70+ }
71+ _ , _ , catalogName , _ = helpers .NewCatalogAndClusterBundles (ctx , replacements ,
72+ singleownindex .AssetNames , singleownindex .Asset ,
73+ singleownbundle .AssetNames , singleownbundle .Asset ,
74+ )
75+ By (fmt .Sprintf ("catalog %q and package %q are ready" , catalogName , packageName ))
76+
4877 By (fmt .Sprintf ("creating namespace %s" , namespace ))
4978 ns := & corev1.Namespace {
5079 ObjectMeta : metav1.ObjectMeta {
@@ -58,39 +87,39 @@ var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Sk
5887 })
5988
6089 It ("should report error when {services} are not specified" , func (ctx SpecContext ) {
61- runNegativePreflightTest (ctx , scenarioMissingServicePerms , namespace )
90+ runNegativePreflightTest (ctx , scenarioMissingServicePerms , namespace , packageName , catalogName )
6291 })
6392
6493 It ("should report error when {create} verb is not specified" , func (ctx SpecContext ) {
65- runNegativePreflightTest (ctx , scenarioMissingCreateVerb , namespace )
94+ runNegativePreflightTest (ctx , scenarioMissingCreateVerb , namespace , packageName , catalogName )
6695 })
6796
6897 It ("should report error when {ClusterRoleBindings} are not specified" , func (ctx SpecContext ) {
69- runNegativePreflightTest (ctx , scenarioMissingClusterRoleBindingsPerms , namespace )
98+ runNegativePreflightTest (ctx , scenarioMissingClusterRoleBindingsPerms , namespace , packageName , catalogName )
7099 })
71100
72101 It ("should report error when {ConfigMap:resourceNames} are not all specified" , func (ctx SpecContext ) {
73- runNegativePreflightTest (ctx , scenarioMissingNamedConfigMapPerms , namespace )
102+ runNegativePreflightTest (ctx , scenarioMissingNamedConfigMapPerms , namespace , packageName , catalogName )
74103 })
75104
76105 It ("should report error when {clusterextension/finalizer} is not specified" , func (ctx SpecContext ) {
77106 helpers .RequireFeatureGateDisabled (features .FeatureGateNewOLMBoxCutterRuntime )
78- runNegativePreflightTest (ctx , scenarioMissingClusterExtensionsFinalizerPerms , namespace )
107+ runNegativePreflightTest (ctx , scenarioMissingClusterExtensionsFinalizerPerms , namespace , packageName , catalogName )
79108 })
80109
81110 It ("should report error when {clusterextensionrevisions/finalizer} is not specified" , func (ctx SpecContext ) {
82111 helpers .RequireFeatureGateEnabled (features .FeatureGateNewOLMBoxCutterRuntime )
83- runNegativePreflightTest (ctx , scenarioMissingClusterExtensionRevisionsFinalizerPerms , namespace )
112+ runNegativePreflightTest (ctx , scenarioMissingClusterExtensionRevisionsFinalizerPerms , namespace , packageName , catalogName )
84113 })
85114
86115 It ("should report error when {escalate, bind} is not specified" , func (ctx SpecContext ) {
87- runNegativePreflightTest (ctx , scenarioMissingEscalateAndBindPerms , namespace )
116+ runNegativePreflightTest (ctx , scenarioMissingEscalateAndBindPerms , namespace , packageName , catalogName )
88117 })
89118})
90119
91- // runNegativePreflightTest creates a deficient ClusterRole and a ClusterExtension that
92- // relies on it , then waits for the expected preflight failure.
93- func runNegativePreflightTest (ctx context.Context , scenario preflightAuthTestScenario , namespace string ) {
120+ // runNegativePreflightTest creates a ClusterRole that is missing one required permission,
121+ // a ClusterExtension that uses it (via the in-cluster catalog) , then waits for the preflight failure.
122+ func runNegativePreflightTest (ctx context.Context , scenario preflightAuthTestScenario , namespace , packageName , catalogName string ) {
94123 k8sClient := env .Get ().K8sClient
95124 unique := rand .String (8 )
96125
@@ -121,28 +150,35 @@ func runNegativePreflightTest(ctx context.Context, scenario preflightAuthTestSce
121150 _ = k8sClient .Delete (ctx , crb )
122151 })
123152
124- // Step 4: Create ClusterExtension referencing that SA
125- ce := helpers .NewClusterExtensionObject ("openshift-pipelines-operator-rh" , "1.15.0" , ceName , saName , namespace )
153+ // Step 4: Create ClusterExtension for that SA using the in-cluster catalog.
154+ // Set watchNamespace in config so the controller can run preflight; otherwise it fails on config validation first.
155+ ce := helpers .NewClusterExtensionObject (packageName , preflightBundleVersion , ceName , saName , namespace , helpers .WithCatalogNameSelector (catalogName ))
156+ ce .Spec .Config = & olmv1.ClusterExtensionConfig {
157+ ConfigType : "Inline" ,
158+ Inline : & apiextensionsv1.JSON {
159+ Raw : []byte (fmt .Sprintf (`{"watchNamespace": "%s"}` , namespace )),
160+ },
161+ }
126162 Expect (k8sClient .Create (ctx , ce )).To (Succeed (), "failed to create ClusterExtension" )
127163 DeferCleanup (func (ctx SpecContext ) {
128164 _ = k8sClient .Delete (ctx , ce )
129165 })
130166
131- // Step 5: Wait for failure
167+ // Step 5: Wait for the controller to report preflight failure.
168+ // The error is in the Progressing condition. We only check the message, not True/False, so the test stays stable.
132169 By ("waiting for ClusterExtension to report preflight failure" )
133170 Eventually (func (g Gomega ) {
134171 latest := & olmv1.ClusterExtension {}
135172 err := k8sClient .Get (ctx , client.ObjectKey {Name : ce .Name }, latest )
136173 g .Expect (err ).NotTo (HaveOccurred ())
137174
138- c := meta .FindStatusCondition (latest .Status .Conditions , "Progressing" )
139- g .Expect (c ).NotTo (BeNil ())
140- g .Expect (c .Status ).To (Equal (metav1 .ConditionTrue ))
141- g .Expect (c .Message ).To (ContainSubstring ("pre-authorization failed" ))
175+ c := meta .FindStatusCondition (latest .Status .Conditions , olmv1 .TypeProgressing )
176+ g .Expect (c ).NotTo (BeNil (), "Progressing condition should be set" )
177+ g .Expect (c .Message ).To (ContainSubstring ("pre-authorization failed" ), "message should report pre-authorization failure" )
142178 }).WithTimeout (helpers .DefaultTimeout ).WithPolling (helpers .DefaultPolling ).Should (Succeed ())
143179}
144180
145- // createDeficientClusterRole returns a modified ClusterRole according to the test scenario.
181+ // createDeficientClusterRole returns a ClusterRole that is missing one permission needed by the test scenario.
146182func createDeficientClusterRole (scenario preflightAuthTestScenario , name , ceName string ) * rbacv1.ClusterRole {
147183 var baseRules []rbacv1.PolicyRule
148184 if helpers .IsFeatureGateEnabled (features .FeatureGateNewOLMBoxCutterRuntime ) {
@@ -189,7 +225,7 @@ func createDeficientClusterRole(scenario preflightAuthTestScenario, name, ceName
189225
190226 switch scenario {
191227 case scenarioMissingServicePerms :
192- // Remove ' services' and ' services/finalizers'
228+ // Remove services and services/finalizers so preflight fails.
193229 for i , r := range rules {
194230 if r .APIGroups [0 ] == "" {
195231 filtered := []string {}
@@ -202,7 +238,7 @@ func createDeficientClusterRole(scenario preflightAuthTestScenario, name, ceName
202238 }
203239 }
204240 case scenarioMissingCreateVerb :
205- // Remove ' create' verb
241+ // Remove the create verb so preflight fails.
206242 for i , r := range rules {
207243 if r .APIGroups [0 ] == "" {
208244 filtered := []string {}
@@ -215,7 +251,7 @@ func createDeficientClusterRole(scenario preflightAuthTestScenario, name, ceName
215251 }
216252 }
217253 case scenarioMissingClusterRoleBindingsPerms :
218- // Remove ' clusterrolebindings'
254+ // Remove clusterrolebindings so preflight fails.
219255 for i , r := range rules {
220256 if r .APIGroups [0 ] == "rbac.authorization.k8s.io" {
221257 filtered := []string {}
@@ -228,26 +264,37 @@ func createDeficientClusterRole(scenario preflightAuthTestScenario, name, ceName
228264 }
229265 }
230266 case scenarioMissingNamedConfigMapPerms :
231- // Restrict configmaps to named subset (resourceNames)
232- for i , r := range rules {
233- if r .APIGroups [0 ] == "" {
267+ // Allow only one ClusterRole by name so the SA cannot manage the rest; preflight then fails.
268+ // The singleown bundle uses ClusterRoles like webhook-operator-metrics-reader.
269+ for i := range rules {
270+ if rules [i ].APIGroups [0 ] == "rbac.authorization.k8s.io" {
234271 filtered := []string {}
235- for _ , res := range r .Resources {
236- if res != "configmaps " && res != "configmaps /finalizers" {
272+ for _ , res := range rules [ i ] .Resources {
273+ if res != "clusterroles " && res != "clusterroles /finalizers" {
237274 filtered = append (filtered , res )
238275 }
239276 }
240277 rules [i ].Resources = filtered
241278 rules = append (rules , rbacv1.PolicyRule {
242- APIGroups : []string {"" },
243- Resources : []string {"configmaps " },
244- Verbs : r . Verbs ,
245- ResourceNames : []string {"config-logging" , "tekton-config-defaults" , "tekton-config-observability " },
279+ APIGroups : []string {"rbac.authorization.k8s.io " },
280+ Resources : []string {"clusterroles" , "clusterroles/finalizers " },
281+ Verbs : [] string { "delete" , "deletecollection" , "create" , "patch" , "get" , "list" , "update" , "watch" } ,
282+ ResourceNames : []string {"webhook-operator-metrics-reader " },
246283 })
284+ break
247285 }
248286 }
249287 case scenarioMissingClusterExtensionsFinalizerPerms :
250- // Remove olm.operatorframework.io permission for finalizers
288+ // Remove permission for clusterextensions/finalizers so preflight fails.
289+ filtered := []rbacv1.PolicyRule {}
290+ for _ , r := range rules {
291+ if len (r .APIGroups ) != 1 || r .APIGroups [0 ] != "olm.operatorframework.io" {
292+ filtered = append (filtered , r )
293+ }
294+ }
295+ rules = filtered
296+ case scenarioMissingClusterExtensionRevisionsFinalizerPerms :
297+ // Remove permission for clusterextensionrevisions/finalizers so preflight fails.
251298 filtered := []rbacv1.PolicyRule {}
252299 for _ , r := range rules {
253300 if len (r .APIGroups ) != 1 || r .APIGroups [0 ] != "olm.operatorframework.io" {
@@ -256,7 +303,7 @@ func createDeficientClusterRole(scenario preflightAuthTestScenario, name, ceName
256303 }
257304 rules = filtered
258305 case scenarioMissingEscalateAndBindPerms :
259- // Remove ' bind' and ' escalate' verbs
306+ // Remove bind and escalate verbs so preflight fails.
260307 for i , r := range rules {
261308 if r .APIGroups [0 ] == "rbac.authorization.k8s.io" {
262309 filtered := []string {}
0 commit comments