Skip to content

Commit a2ae884

Browse files
camilamacedo86ci-robot
authored andcommitted
UPSTREAM: <carry>: [OTE] Migrate preflight checks from openshift/origin
Migrated OLMv1 operator preflight checks from using external YAML files to defining ClusterRole permissions directly in Go structs. This improves test reliability and simplifies test setup by removing file dependencies. The changes ensure precise replication of original test scenarios, including specific permission omissions for services, create verbs, ClusterRoleBindings, ConfigMap resourceNames, and escalate/bind verbs. Assisted-by: Gemini
1 parent 5953600 commit a2ae884

File tree

2 files changed

+308
-0
lines changed

2 files changed

+308
-0
lines changed

openshift/tests-extension/.openshift-tests-extension/openshift_payload_olmv1.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,66 @@
99
"lifecycle": "blocking",
1010
"environmentSelector": {}
1111
},
12+
{
13+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {services} are not specified",
14+
"labels": {},
15+
"resources": {
16+
"isolation": {}
17+
},
18+
"source": "openshift:payload:olmv1",
19+
"lifecycle": "blocking",
20+
"environmentSelector": {}
21+
},
22+
{
23+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {create} verb is not specified",
24+
"labels": {},
25+
"resources": {
26+
"isolation": {}
27+
},
28+
"source": "openshift:payload:olmv1",
29+
"lifecycle": "blocking",
30+
"environmentSelector": {}
31+
},
32+
{
33+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {ClusterRoleBindings} are not specified",
34+
"labels": {},
35+
"resources": {
36+
"isolation": {}
37+
},
38+
"source": "openshift:payload:olmv1",
39+
"lifecycle": "blocking",
40+
"environmentSelector": {}
41+
},
42+
{
43+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {ConfigMap:resourceNames} are not all specified",
44+
"labels": {},
45+
"resources": {
46+
"isolation": {}
47+
},
48+
"source": "openshift:payload:olmv1",
49+
"lifecycle": "blocking",
50+
"environmentSelector": {}
51+
},
52+
{
53+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {clusterextension/finalizer} is not specified",
54+
"labels": {},
55+
"resources": {
56+
"isolation": {}
57+
},
58+
"source": "openshift:payload:olmv1",
59+
"lifecycle": "blocking",
60+
"environmentSelector": {}
61+
},
62+
{
63+
"name": "[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks should report error when {escalate, bind} is not specified",
64+
"labels": {},
65+
"resources": {
66+
"isolation": {}
67+
},
68+
"source": "openshift:payload:olmv1",
69+
"lifecycle": "blocking",
70+
"environmentSelector": {}
71+
},
1272
{
1373
"name": "[sig-olmv1] OLMv1 should pass a trivial sanity check",
1474
"labels": {},
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
package test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
//nolint:staticcheck // ST1001: dot-imports for readability
9+
. "github.com/onsi/ginkgo/v2"
10+
//nolint:staticcheck // ST1001: dot-imports for readability
11+
. "github.com/onsi/gomega"
12+
13+
corev1 "k8s.io/api/core/v1"
14+
rbacv1 "k8s.io/api/rbac/v1"
15+
"k8s.io/apimachinery/pkg/api/meta"
16+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
"k8s.io/apimachinery/pkg/util/rand"
18+
"sigs.k8s.io/controller-runtime/pkg/client"
19+
20+
olmv1 "github.com/operator-framework/operator-controller/api/v1"
21+
22+
"github/operator-framework-operator-controller/openshift/tests-extension/pkg/env"
23+
"github/operator-framework-operator-controller/openshift/tests-extension/pkg/helpers"
24+
)
25+
26+
var _ = Describe("[sig-olmv1][OCPFeatureGate:NewOLMPreflightPermissionChecks][Skipped:Disconnected] OLMv1 operator preflight checks", func() {
27+
var (
28+
namespace string
29+
k8sClient client.Client
30+
)
31+
BeforeEach(func() {
32+
helpers.RequireOLMv1CapabilityOnOpenshift()
33+
k8sClient = env.Get().K8sClient
34+
namespace = "preflight-test-ns-" + rand.String(4)
35+
36+
By(fmt.Sprintf("creating namespace %s", namespace))
37+
ns := &corev1.Namespace{
38+
ObjectMeta: metav1.ObjectMeta{
39+
Name: namespace,
40+
},
41+
}
42+
Expect(k8sClient.Create(context.Background(), ns)).To(Succeed(), "failed to create test namespace")
43+
DeferCleanup(func() {
44+
_ = k8sClient.Delete(context.Background(), ns)
45+
})
46+
})
47+
48+
It("should report error when {services} are not specified", func(ctx SpecContext) {
49+
runNegativePreflightTest(ctx, 0, namespace)
50+
})
51+
52+
It("should report error when {create} verb is not specified", func(ctx SpecContext) {
53+
runNegativePreflightTest(ctx, 1, namespace)
54+
})
55+
56+
It("should report error when {ClusterRoleBindings} are not specified", func(ctx SpecContext) {
57+
runNegativePreflightTest(ctx, 2, namespace)
58+
})
59+
60+
It("should report error when {ConfigMap:resourceNames} are not all specified", func(ctx SpecContext) {
61+
runNegativePreflightTest(ctx, 3, namespace)
62+
})
63+
64+
It("should report error when {clusterextension/finalizer} is not specified", func(ctx SpecContext) {
65+
runNegativePreflightTest(ctx, 4, namespace)
66+
})
67+
68+
It("should report error when {escalate, bind} is not specified", func(ctx SpecContext) {
69+
runNegativePreflightTest(ctx, 5, namespace)
70+
})
71+
})
72+
73+
// runNegativePreflightTest creates a deficient ClusterRole and a ClusterExtension that
74+
// relies on it, then waits for the expected preflight failure.
75+
func runNegativePreflightTest(ctx context.Context, scenario int, namespace string) {
76+
k8sClient := env.Get().K8sClient
77+
unique := rand.String(8)
78+
79+
// Define names
80+
crName := fmt.Sprintf("install-test-cr-%s", unique)
81+
saName := fmt.Sprintf("install-test-sa-%s", unique)
82+
crbName := fmt.Sprintf("install-test-crb-%s", unique)
83+
ceName := fmt.Sprintf("install-test-ce-%s", unique)
84+
85+
// Step 1: Create deficient ClusterRole
86+
defCR := createDeficientClusterRole(scenario, crName, ceName)
87+
Expect(k8sClient.Create(ctx, defCR)).To(Succeed(), "failed to create ClusterRole")
88+
DeferCleanup(func(ctx SpecContext) {
89+
_ = k8sClient.Delete(ctx, defCR)
90+
})
91+
92+
// Step 2: Create matching ServiceAccount
93+
sa := helpers.NewServiceAccount(saName, namespace)
94+
Expect(k8sClient.Create(ctx, sa)).To(Succeed(), "failed to create ServiceAccount")
95+
DeferCleanup(func(ctx SpecContext) {
96+
_ = k8sClient.Delete(ctx, sa)
97+
})
98+
99+
// Step 3: Bind SA to the deficient ClusterRole
100+
crb := helpers.NewClusterRoleBinding(crbName, crName, saName, namespace)
101+
Expect(k8sClient.Create(ctx, crb)).To(Succeed(), "failed to create ClusterRoleBinding")
102+
DeferCleanup(func(ctx SpecContext) {
103+
_ = k8sClient.Delete(ctx, crb)
104+
})
105+
106+
// Step 4: Create ClusterExtension referencing that SA
107+
ce := helpers.NewClusterExtensionObject("openshift-pipelines-operator-rh", "1.15.0", ceName, saName, namespace)
108+
Expect(k8sClient.Create(ctx, ce)).To(Succeed(), "failed to create ClusterExtension")
109+
DeferCleanup(func(ctx SpecContext) {
110+
_ = k8sClient.Delete(ctx, ce)
111+
})
112+
113+
// Step 5: Wait for failure
114+
By("waiting for ClusterExtension to report preflight failure")
115+
Eventually(func(g Gomega) {
116+
latest := &olmv1.ClusterExtension{}
117+
err := k8sClient.Get(ctx, client.ObjectKey{Name: ce.Name}, latest)
118+
g.Expect(err).NotTo(HaveOccurred())
119+
120+
c := meta.FindStatusCondition(latest.Status.Conditions, "Progressing")
121+
g.Expect(c).NotTo(BeNil())
122+
g.Expect(c.Status).To(Equal(metav1.ConditionTrue))
123+
g.Expect(c.Message).To(ContainSubstring("pre-authorization failed"))
124+
125+
c = meta.FindStatusCondition(latest.Status.Conditions, "Installed")
126+
g.Expect(c).NotTo(BeNil())
127+
g.Expect(c.Status).To(Equal(metav1.ConditionFalse))
128+
}).WithTimeout(5 * time.Minute).WithPolling(5 * time.Second).Should(Succeed())
129+
}
130+
131+
// createDeficientClusterRole returns a modified ClusterRole according to the test scenario.
132+
func createDeficientClusterRole(scenario int, name, ceName string) *rbacv1.ClusterRole {
133+
baseRules := []rbacv1.PolicyRule{
134+
{
135+
APIGroups: []string{"olm.operatorframework.io"},
136+
Resources: []string{"clusterextensions/finalizers"},
137+
Verbs: []string{"update"},
138+
ResourceNames: []string{ceName},
139+
},
140+
{
141+
APIGroups: []string{""},
142+
Resources: []string{"nodes"},
143+
Verbs: []string{"list"},
144+
},
145+
{
146+
APIGroups: []string{""},
147+
Resources: []string{"pods", "pods/finalizers", "services", "services/finalizers", "endpoints", "endpoints/finalizers", "persistentvolumeclaims", "persistentvolumeclaims/finalizers", "events", "events/finalizers", "configmaps", "configmaps/finalizers", "secrets", "secrets/finalizers", "pods/log", "limitranges", "limitranges/finalizers", "namespaces", "namespaces/finalizers", "serviceaccounts", "serviceaccounts/finalizers"},
148+
Verbs: []string{"delete", "deletecollection", "create", "patch", "get", "list", "update", "watch"},
149+
},
150+
{
151+
APIGroups: []string{"rbac.authorization.k8s.io"},
152+
Resources: []string{"clusterroles", "clusterroles/finalizers", "roles", "roles/finalizers", "clusterrolebindings", "clusterrolebindings/finalizers", "rolebindings", "rolebindings/finalizers"},
153+
Verbs: []string{"delete", "deletecollection", "create", "patch", "get", "list", "update", "watch", "bind", "escalate"},
154+
},
155+
}
156+
157+
// Copy rules to avoid mutation
158+
rules := make([]rbacv1.PolicyRule, len(baseRules))
159+
copy(rules, baseRules)
160+
161+
switch scenario {
162+
case 0:
163+
// Remove 'services' and 'services/finalizers'
164+
for i, r := range rules {
165+
if r.APIGroups[0] == "" {
166+
filtered := []string{}
167+
for _, res := range r.Resources {
168+
if res != "services" && res != "services/finalizers" {
169+
filtered = append(filtered, res)
170+
}
171+
}
172+
rules[i].Resources = filtered
173+
}
174+
}
175+
case 1:
176+
// Remove 'create' verb
177+
for i, r := range rules {
178+
if r.APIGroups[0] == "" {
179+
filtered := []string{}
180+
for _, v := range r.Verbs {
181+
if v != "create" {
182+
filtered = append(filtered, v)
183+
}
184+
}
185+
rules[i].Verbs = filtered
186+
}
187+
}
188+
case 2:
189+
// Remove 'clusterrolebindings'
190+
for i, r := range rules {
191+
if r.APIGroups[0] == "rbac.authorization.k8s.io" {
192+
filtered := []string{}
193+
for _, res := range r.Resources {
194+
if res != "clusterrolebindings" && res != "clusterrolebindings/finalizers" {
195+
filtered = append(filtered, res)
196+
}
197+
}
198+
rules[i].Resources = filtered
199+
}
200+
}
201+
case 3:
202+
// Restrict configmaps to named subset (resourceNames)
203+
for i, r := range rules {
204+
if r.APIGroups[0] == "" {
205+
filtered := []string{}
206+
for _, res := range r.Resources {
207+
if res != "configmaps" && res != "configmaps/finalizers" {
208+
filtered = append(filtered, res)
209+
}
210+
}
211+
rules[i].Resources = filtered
212+
rules = append(rules, rbacv1.PolicyRule{
213+
APIGroups: []string{""},
214+
Resources: []string{"configmaps"},
215+
Verbs: r.Verbs,
216+
ResourceNames: []string{"config-logging", "tekton-config-defaults", "tekton-config-observability"},
217+
})
218+
}
219+
}
220+
case 4:
221+
// Remove olm.operatorframework.io permission for finalizers
222+
filtered := []rbacv1.PolicyRule{}
223+
for _, r := range rules {
224+
if len(r.APIGroups) != 1 || r.APIGroups[0] != "olm.operatorframework.io" {
225+
filtered = append(filtered, r)
226+
}
227+
}
228+
rules = filtered
229+
case 5:
230+
// Remove 'bind' and 'escalate' verbs
231+
for i, r := range rules {
232+
if r.APIGroups[0] == "rbac.authorization.k8s.io" {
233+
filtered := []string{}
234+
for _, v := range r.Verbs {
235+
if v != "bind" && v != "escalate" {
236+
filtered = append(filtered, v)
237+
}
238+
}
239+
rules[i].Verbs = filtered
240+
}
241+
}
242+
}
243+
244+
return &rbacv1.ClusterRole{
245+
ObjectMeta: metav1.ObjectMeta{Name: name},
246+
Rules: rules,
247+
}
248+
}

0 commit comments

Comments
 (0)