@@ -7,6 +7,7 @@ package osde2etests
77import (
88 "context"
99 "fmt"
10+ "os"
1011 "strings"
1112 "time"
1213
@@ -53,26 +54,44 @@ var _ = Describe("Configure AlertManager Operator", Ordered, func() {
5354
5455 cfg , err := config .GetConfig ()
5556 Expect (err ).Should (BeNil (), "failed to get kubeconfig" )
57+
58+ if impersonateUser := os .Getenv ("E2E_IMPERSONATE_USER" ); impersonateUser != "" {
59+ GinkgoLogr .Info ("Impersonating user from E2E_IMPERSONATE_USER" , "user" , impersonateUser )
60+ cfg .Impersonate .UserName = impersonateUser
61+ }
62+
5663 client , err = resources .New (cfg )
5764 Expect (err ).Should (BeNil (), "resources.New error" )
5865
5966 dynamicClient , err = dynamic .NewForConfig (cfg )
6067 Expect (err ).ShouldNot (HaveOccurred (), "failed to configure Dynamic client" )
6168
62- // Create openshift client locally for prometheus client setup
6369 k8s , err := openshift .New (GinkgoLogr )
64- Expect (err ).ShouldNot (HaveOccurred (), "unable to setup openshift client" )
65-
66- prom , err = prometheus .New (ctx , k8s )
67- Expect (err ).ShouldNot (HaveOccurred (), "unable to setup prometheus client" )
70+ if err != nil {
71+ if os .Getenv ("E2E_READ_ONLY" ) == "true" {
72+ GinkgoLogr .Info ("OpenShift client unavailable (read-only mode) - metric tests will be skipped" , "error" , err )
73+ } else {
74+ Expect (err ).ShouldNot (HaveOccurred (), "unable to setup openshift client" )
75+ }
76+ } else {
77+ prom , err = prometheus .New (ctx , k8s )
78+ if err != nil {
79+ if os .Getenv ("E2E_READ_ONLY" ) == "true" {
80+ GinkgoLogr .Info ("Prometheus client unavailable (read-only mode) - metric tests will be skipped" , "error" , err )
81+ prom = nil
82+ } else {
83+ Expect (err ).ShouldNot (HaveOccurred (), "unable to setup prometheus client" )
84+ }
85+ }
86+ }
6887
6988 // Detect deployment method (OLM, PKO, both, or unknown)
7089 deploymentMethod = detectDeploymentMethod (ctx , dynamicClient , namespace , operatorName )
7190 GinkgoLogr .Info ("Detected deployment method" , "method" , deploymentMethod )
7291 GinkgoWriter .Printf ("Deployment method: %s\n " , deploymentMethod )
7392 })
7493 // Validate deployment method and ensure only one method is active
75- It ("has valid deployment method and correct resources" , func (ctx context.Context ) {
94+ It ("has valid deployment method and correct resources" , Label ( "read-only" ), func (ctx context.Context ) {
7695 switch deploymentMethod {
7796 case "both" :
7897 Fail (`CRITICAL: Both OLM and PKO deployment artifacts detected simultaneously!
@@ -140,19 +159,19 @@ POSSIBLE CAUSES:
140159 }
141160 })
142161
143- It ("service accounts exist" , func (ctx context.Context ) {
162+ It ("service accounts exist" , Label ( "read-only" ), func (ctx context.Context ) {
144163 for _ , serviceAccount := range serviceAccounts {
145164 err := client .Get (ctx , serviceAccount , namespace , & v1.ServiceAccount {})
146165 Expect (err ).ShouldNot (HaveOccurred (), "Service account %s not found" , serviceAccount )
147166 }
148167 })
149168
150- It ("deployment exists" , func (ctx context.Context ) {
169+ It ("deployment exists" , Label ( "read-only" ), func (ctx context.Context ) {
151170 err := wait .For (conditions .New (client ).DeploymentAvailable (operatorName , namespace ))
152171 Expect (err ).ShouldNot (HaveOccurred (), "Deployment %s not available" , operatorName )
153172 })
154173
155- It ("roles exist" , func (ctx context.Context ) {
174+ It ("roles exist" , Label ( "read-only" ), func (ctx context.Context ) {
156175 if deploymentMethod == "olm" {
157176 var roles rbacv1.RoleList
158177 err := client .WithNamespace (namespace ).List (ctx , & roles , resources .WithLabelSelector (labelSelector ))
@@ -164,7 +183,7 @@ POSSIBLE CAUSES:
164183 }
165184 })
166185
167- It ("role bindings exist" , func (ctx context.Context ) {
186+ It ("role bindings exist" , Label ( "read-only" ), func (ctx context.Context ) {
168187 if deploymentMethod == "olm" {
169188 var roleBindings rbacv1.RoleBindingList
170189 err := client .WithNamespace (namespace ).List (ctx , & roleBindings , resources .WithLabelSelector (labelSelector ))
@@ -176,7 +195,7 @@ POSSIBLE CAUSES:
176195 }
177196 })
178197
179- It ("cluster roles exist" , func (ctx context.Context ) {
198+ It ("cluster roles exist" , Label ( "read-only" ), func (ctx context.Context ) {
180199 if deploymentMethod == "olm" {
181200 var clusterRoles rbacv1.ClusterRoleList
182201 err := client .WithNamespace (namespace ).List (ctx , & clusterRoles , resources .WithLabelSelector (labelSelector ))
@@ -190,7 +209,7 @@ POSSIBLE CAUSES:
190209 }
191210 })
192211
193- It ("cluster role bindings exist" , func (ctx context.Context ) {
212+ It ("cluster role bindings exist" , Label ( "read-only" ), func (ctx context.Context ) {
194213 if deploymentMethod == "olm" {
195214 var clusterRoleBindings rbacv1.ClusterRoleBindingList
196215 err := client .List (ctx , & clusterRoleBindings , resources .WithLabelSelector (labelSelector ))
@@ -204,19 +223,19 @@ POSSIBLE CAUSES:
204223 }
205224 })
206225
207- It ("config map exists" , func (ctx context.Context ) {
226+ It ("config map exists" , Label ( "read-only" ), func (ctx context.Context ) {
208227 err := client .Get (ctx , configMapLockFile , namespace , & v1.ConfigMap {})
209228 Expect (err ).ShouldNot (HaveOccurred (), "Failed to get config map %s" , configMapLockFile )
210229 })
211230
212- It ("secrets exist" , func (ctx context.Context ) {
231+ It ("secrets exist" , Label ( "read-only" ), func (ctx context.Context ) {
213232 for _ , secret := range secrets {
214233 err := client .Get (ctx , secret , namespace , & v1.Secret {})
215234 Expect (err ).ShouldNot (HaveOccurred (), "Secret %s not found" , secret )
216235 }
217236 })
218237
219- It ("alertmanager-main secret contains valid configuration" , func (ctx context.Context ) {
238+ It ("alertmanager-main secret contains valid configuration" , Label ( "read-only" ), func (ctx context.Context ) {
220239 // Get the alertmanager-main secret from openshift-monitoring
221240 var secret v1.Secret
222241 err := client .Get (ctx , "alertmanager-main" , namespace , & secret )
@@ -233,7 +252,10 @@ POSSIBLE CAUSES:
233252 Expect (err ).ShouldNot (HaveOccurred (), "alertmanager config validation failed: %v" , err )
234253 })
235254
236- It ("validation metric exists and shows config is valid" , func (ctx context.Context ) {
255+ It ("validation metric exists and shows config is valid" , Label ("read-only" ), func (ctx context.Context ) {
256+ if prom == nil {
257+ Skip ("Prometheus client unavailable - skipping metric test" )
258+ }
237259 // Query the alertmanager_config_validation_failed metric
238260 // Metric value: 0 = validation succeeded, 1 = validation failed
239261 query := `alertmanager_config_validation_failed{name="configure-alertmanager-operator"}`
@@ -266,7 +288,7 @@ POSSIBLE CAUSES:
266288 // These tests validate that cluster-scoped resources remain accessible
267289 // when the operator cache is scoped to openshift-monitoring namespace
268290
269- It ("can access ClusterVersion cluster-scoped resource" , func (ctx context.Context ) {
291+ It ("can access ClusterVersion cluster-scoped resource" , Label ( "read-only" ), func (ctx context.Context ) {
270292 ginkgo .By ("Verifying operator can read ClusterVersion despite namespace-scoped cache" )
271293
272294 // ClusterVersion is a cluster-scoped resource that the operator needs for:
@@ -394,7 +416,7 @@ ClusterVersion spec: %+v
394416 GinkgoLogr .Info ("ClusterVersion access verified" , "clusterID" , clusterID )
395417 })
396418
397- It ("can access Proxy cluster-scoped resource" , func (ctx context.Context ) {
419+ It ("can access Proxy cluster-scoped resource" , Label ( "read-only" ), func (ctx context.Context ) {
398420 ginkgo .By ("Verifying operator can read cluster Proxy configuration" )
399421
400422 // Proxy is a cluster-scoped resource that the operator needs for:
@@ -523,7 +545,7 @@ NEXT STEPS:
523545 }
524546 })
525547
526- It ("can access Infrastructure cluster-scoped resource" , func (ctx context.Context ) {
548+ It ("can access Infrastructure cluster-scoped resource" , Label ( "read-only" ), func (ctx context.Context ) {
527549 ginkgo .By ("Verifying operator can read Infrastructure and detect cluster type" )
528550
529551 // Infrastructure is a cluster-scoped resource that the operator needs for:
@@ -723,7 +745,7 @@ If ConfigMap is intentionally missing, this warning can be ignored.
723745 }
724746 })
725747
726- It ("operator pod memory usage is within expected limits" , func (ctx context.Context ) {
748+ It ("operator pod memory usage is within expected limits" , Label ( "read-only" ), func (ctx context.Context ) {
727749 ginkgo .By ("Verifying namespace scoping reduces operator memory footprint" )
728750
729751 // With namespace scoping, the operator should use significantly less memory
@@ -875,7 +897,7 @@ Current Pod: %s (Age: %s)
875897 )
876898 })
877899
878- It ("routes alerts for management cluster namespaces when on MC cluster" , func (ctx context.Context ) {
900+ It ("routes alerts for management cluster namespaces when on MC cluster" , Label ( "read-only" ), func (ctx context.Context ) {
879901 ginkgo .By ("Checking if this is a HyperShift Management Cluster" )
880902
881903 // First, determine if this is a management cluster
@@ -1252,7 +1274,7 @@ Alerts from hosted control plane namespaces will be routed correctly.
12521274 }
12531275 })
12541276
1255- Describe ("Alertmanager configuration structure" , func () {
1277+ Describe ("Alertmanager configuration structure" , Label ( "read-only" ), func () {
12561278 It ("has required config structure with global, route, and receivers" , func (ctx context.Context ) {
12571279 configBytes , err := utils .GetAlertmanagerConfigBytes (ctx , client , namespace )
12581280 Expect (err ).ShouldNot (HaveOccurred (), "failed to get alertmanager config from secret" )
@@ -1281,7 +1303,7 @@ Alerts from hosted control plane namespaces will be routed correctly.
12811303 })
12821304 })
12831305
1284- Describe ("PagerDuty receiver configuration" , func () {
1306+ Describe ("PagerDuty receiver configuration" , Label ( "read-only" ), func () {
12851307 It ("contains pagerduty receiver when pd-secret exists" , func (ctx context.Context ) {
12861308 configBytes , err := utils .GetAlertmanagerConfigBytes (ctx , client , namespace )
12871309 Expect (err ).ShouldNot (HaveOccurred (), "failed to get alertmanager config from secret" )
@@ -1314,7 +1336,7 @@ Alerts from hosted control plane namespaces will be routed correctly.
13141336 })
13151337 })
13161338
1317- Describe ("Alert routing rules" , func () {
1339+ Describe ("Alert routing rules" , Label ( "read-only" ), func () {
13181340 It ("contains null receiver for alert suppression" , func (ctx context.Context ) {
13191341 configBytes , err := utils .GetAlertmanagerConfigBytes (ctx , client , namespace )
13201342 Expect (err ).ShouldNot (HaveOccurred (), "failed to get alertmanager config from secret" )
@@ -1355,7 +1377,11 @@ Alerts from hosted control plane namespaces will be routed correctly.
13551377 // ========================================================================
13561378 // Reconciliation and secret lifecycle tests
13571379 // ========================================================================
1358- Describe ("Reconciliation and secret lifecycle" , Ordered , func () {
1380+ // Some tests in this group are read-only (existence checks, metric queries) but are
1381+ // labeled mutating because they are ordered prerequisites for the mutating tests that
1382+ // follow, and the GoAlert Context uses a BeforeEach that creates/backs up secrets.
1383+ // Moving them out would require a larger refactor of the Ordered test structure.
1384+ Describe ("Reconciliation and secret lifecycle" , Label ("mutating" ), Ordered , func () {
13591385 const (
13601386 reconcileTimeout = 2 * time .Minute
13611387 reconcileInterval = 5 * time .Second
@@ -1612,8 +1638,11 @@ Alerts from hosted control plane namespaces will be routed correctly.
16121638 })
16131639
16141640 // --- Reconciliation metrics ---
1615- Describe ("Reconciliation metrics" , func () {
1641+ Describe ("Reconciliation metrics" , Label ( "read-only" ), func () {
16161642 It ("secret existence metrics reflect cluster state" , func (ctx context.Context ) {
1643+ if prom == nil {
1644+ Skip ("Prometheus client unavailable - skipping metric test" )
1645+ }
16171646 type metricCheck struct {
16181647 query string
16191648 secretName string
0 commit comments