1
+ // nolint:gosec
2
+ // Package e2e contains end-to-end tests to verify that the metrics endpoints
3
+ // for both components. Metrics are exported and accessible by authorized users through
4
+ // RBAC and ServiceAccount tokens.
5
+ //
6
+ // These tests perform the following steps:
7
+ // 1. Create a ClusterRoleBinding to grant necessary permissions for accessing metrics.
8
+ // 2. Generate a ServiceAccount token for authentication.
9
+ // 3. Deploy a curl pod to interact with the metrics endpoint.
10
+ // 4. Wait for the curl pod to become ready.
11
+ // 5. Execute a curl command from the pod to validate the metrics endpoint.
12
+ // 6. Clean up all resources created during the test, such as the ClusterRoleBinding and curl pod.
1
13
package e2e
2
14
3
15
import (
4
16
"bytes"
5
17
"io"
6
18
"os/exec"
7
19
"testing"
20
+ "time"
8
21
9
22
"github.com/stretchr/testify/require"
23
+
24
+ "github.com/operator-framework/operator-controller/test/utils"
10
25
)
11
26
12
- // nolint:gosec
13
27
// TestOperatorControllerMetricsExportedEndpoint verifies that the metrics endpoint for the operator controller
14
- // is exported correctly and accessible by authorized users through RBAC and a ServiceAccount token.
15
- // The test performs the following steps:
16
- // 1. Creates a ClusterRoleBinding to grant necessary permissions for accessing metrics.
17
- // 2. Generates a ServiceAccount token for authentication.
18
- // 3. Deploys a curl pod to interact with the metrics endpoint.
19
- // 4. Waits for the curl pod to become ready.
20
- // 5. Executes a curl command from the pod to validate the metrics endpoint.
21
- // 6. Cleans up all resources created during the test, such as the ClusterRoleBinding and curl pod.
22
28
func TestOperatorControllerMetricsExportedEndpoint (t * testing.T ) {
23
- var (
24
- token string
25
- curlPod = "curl-metrics"
26
- client = ""
27
- clients = []string {"kubectl" , "oc" }
29
+ client := utils .FindK8sClient (t )
30
+ namespace := getNamespace (t , client , "control-plane=operator-controller-controller-manager" )
31
+
32
+ createClusterRoleBinding (
33
+ t , client , "operator-controller-metrics-binding" ,
34
+ "operator-controller-metrics-reader" , namespace ,
35
+ "operator-controller-controller-manager" ,
28
36
)
29
37
30
- t .Log ("Looking for k8s client" )
31
- for _ , c := range clients {
32
- // Would prefer to use `command -v`, but even that may not be installed!
33
- err := exec .Command (c , "version" , "--client" ).Run ()
34
- if err == nil {
35
- client = c
36
- break
37
- }
38
- }
39
- if client == "" {
40
- t .Fatal ("k8s client not found" )
41
- }
42
- t .Logf ("Using %q as k8s client" , client )
38
+ token := createServiceAccountToken (t , client , namespace , "operator-controller-controller-manager" )
39
+
40
+ createCurlPod (t , client , namespace , "oper-curl-metrics" , "operator-controller-controller-manager" )
41
+ waitForPodReady (t , client , namespace , "oper-curl-metrics" )
42
+
43
+ validateMetricsEndpoint (
44
+ t , client , namespace , "oper-curl-metrics" , token ,
45
+ "https://operator-controller-service." + namespace + ".svc.cluster.local:8443/metrics" ,
46
+ )
47
+ }
48
+
49
+ // TestCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for catalogd
50
+ func TestCatalogdMetricsExportedEndpoint (t * testing.T ) {
51
+ client := utils .FindK8sClient (t )
52
+ namespace := getNamespace (t , client , "control-plane=catalogd-controller-manager" )
53
+
54
+ createClusterRoleBinding (
55
+ t , client , "catalogd-metrics-binding" ,
56
+ "catalogd-metrics-reader" , namespace ,
57
+ "catalogd-controller-manager" ,
58
+ )
59
+
60
+ token := createServiceAccountToken (t , client , namespace , "catalogd-controller-manager" )
61
+
62
+ createCurlPod (t , client , namespace , "catalogd-curl-metrics" , "catalogd-controller-manager" )
63
+ waitForPodReady (t , client , namespace , "catalogd-curl-metrics" )
43
64
44
- t .Log ("Determining operator-controller namespace" )
45
- cmd := exec .Command (client , "get" , "pods" , "--all-namespaces" , "--selector=control-plane=operator-controller-controller-manager" , "--output=jsonpath={.items[0].metadata.namespace}" )
65
+ validateMetricsEndpoint (
66
+ t , client , namespace , "catalogd-curl-metrics" , token ,
67
+ "https://catalogd-service." + namespace + ".svc.cluster.local:7443/metrics" ,
68
+ )
69
+ }
70
+
71
+ func getNamespace (t * testing.T , client , selector string ) string {
72
+ cmd := exec .Command (client , "get" , "pods" , "--all-namespaces" , "--selector=" + selector , "--output=jsonpath={.items[0].metadata.namespace}" )
46
73
output , err := cmd .CombinedOutput ()
47
- require .NoError (t , err , "Error creating determining operator-controller namespace: %s" , string (output ))
48
- namespace := string (output )
74
+ require .NoError (t , err , "Error determining namespace: %s" , string (output ))
75
+
76
+ namespace := string (bytes .TrimSpace (output ))
49
77
if namespace == "" {
50
- t .Fatal ("No operator-controller namespace found" )
78
+ t .Fatal ("No namespace found for selector " + selector )
51
79
}
52
- t .Logf ("Using %q as operator-controller namespace" , namespace )
80
+ return namespace
81
+ }
53
82
54
- t .Log ("Creating ClusterRoleBinding for operator controller metrics" )
55
- cmd = exec .Command (client , "create" , "clusterrolebinding" , "operator-controller-metrics-binding" ,
56
- "--clusterrole=operator-controller-metrics-reader" ,
57
- "--serviceaccount=" + namespace + ":operator-controller-controller-manager" )
58
- output , err = cmd .CombinedOutput ()
83
+ func createClusterRoleBinding (t * testing.T , client , name , clusterRole , namespace , serviceAccount string ) {
84
+ t .Logf ("Creating ClusterRoleBinding %s" , name )
85
+ cmd := exec .Command (client , "create" , "clusterrolebinding" , name ,
86
+ "--clusterrole=" + clusterRole ,
87
+ "--serviceaccount=" + namespace + ":" + serviceAccount )
88
+ output , err := cmd .CombinedOutput ()
59
89
require .NoError (t , err , "Error creating ClusterRoleBinding: %s" , string (output ))
60
90
61
91
defer func () {
62
- t .Log ("Cleaning up ClusterRoleBinding" )
63
- _ = exec .Command (client , "delete" , "clusterrolebinding" , "operator-controller-metrics-binding" , "--ignore-not-found=true" ).Run ()
92
+ t .Logf ("Cleaning up ClusterRoleBinding %s" , name )
93
+ _ = exec .Command (client , "delete" , "clusterrolebinding" , name , "--ignore-not-found=true" ).Run ()
64
94
}()
95
+ }
65
96
97
+ func createServiceAccountToken (t * testing.T , client , namespace , serviceAccount string ) string {
66
98
t .Log ("Generating ServiceAccount token" )
67
- tokenCmd := exec .Command (client , "create" , "token" , "operator-controller-controller-manager" , "-n" , namespace )
68
- tokenOutput , tokenCombinedOutput , err := stdoutAndCombined (tokenCmd )
99
+ cmd := exec .Command (client , "create" , "token" , serviceAccount , "-n" , namespace )
100
+ tokenOutput , tokenCombinedOutput , err := stdoutAndCombined (cmd )
69
101
require .NoError (t , err , "Error creating token: %s" , string (tokenCombinedOutput ))
70
- token = string (bytes .TrimSpace (tokenOutput ))
102
+ return string (bytes .TrimSpace (tokenOutput ))
103
+ }
71
104
105
+ func createCurlPod (t * testing.T , client , namespace , podName , serviceAccount string ) {
72
106
t .Log ("Creating curl pod to validate the metrics endpoint" )
73
- cmd = exec .Command (client , "run" , curlPod ,
107
+ cmd : = exec .Command (client , "run" , podName ,
74
108
"--image=curlimages/curl:7.87.0" , "-n" , namespace ,
75
109
"--restart=Never" ,
76
110
"--overrides" , `{
@@ -81,154 +115,62 @@ func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
81
115
"command": ["sh", "-c", "sleep 3600"],
82
116
"securityContext": {
83
117
"allowPrivilegeEscalation": false,
84
- "capabilities": {
85
- "drop": ["ALL"]
86
- },
118
+ "capabilities": {"drop": ["ALL"]},
87
119
"runAsNonRoot": true,
88
120
"runAsUser": 1000,
89
- "seccompProfile": {
90
- "type": "RuntimeDefault"
91
- }
121
+ "seccompProfile": {"type": "RuntimeDefault"}
92
122
}
93
123
}],
94
- "serviceAccountName": "operator-controller-controller-manager "
124
+ "serviceAccountName": "` + serviceAccount + ` "
95
125
}
96
126
}` )
97
- output , err = cmd .CombinedOutput ()
127
+ output , err : = cmd .CombinedOutput ()
98
128
require .NoError (t , err , "Error creating curl pod: %s" , string (output ))
99
129
100
130
defer func () {
101
131
t .Log ("Cleaning up curl pod" )
102
- _ = exec .Command (client , "delete" , "pod" , curlPod , "-n" , namespace , "--ignore-not-found=true" ).Run ()
132
+ _ = exec .Command (client , "delete" , "pod" , podName , "-n" , namespace , "--ignore-not-found=true" ).Run ()
103
133
}()
104
-
105
- t .Log ("Waiting for the curl pod to be ready" )
106
- waitCmd := exec .Command (client , "wait" , "--for=condition=Ready" , "pod" , curlPod , "-n" , namespace , "--timeout=60s" )
107
- waitOutput , waitErr := waitCmd .CombinedOutput ()
108
- require .NoError (t , waitErr , "Error waiting for curl pod to be ready: %s" , string (waitOutput ))
109
-
110
- t .Log ("Validating the metrics endpoint" )
111
- metricsURL := "https://operator-controller-service." + namespace + ".svc.cluster.local:8443/metrics"
112
- curlCmd := exec .Command (client , "exec" , curlPod , "-n" , namespace , "--" ,
113
- "curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , metricsURL )
114
- output , err = curlCmd .CombinedOutput ()
115
- require .NoError (t , err , "Error calling metrics endpoint: %s" , string (output ))
116
- require .Contains (t , string (output ), "200 OK" , "Metrics endpoint did not return 200 OK" )
117
134
}
118
135
119
- // nolint:gosec
120
- // TestCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for the catalogd
121
- // is exported correctly and accessible by authorized users through RBAC and a ServiceAccount token.
122
- // The test performs the following steps:
123
- // 1. Creates a ClusterRoleBinding to grant necessary permissions for accessing metrics.
124
- // 2. Generates a ServiceAccount token for authentication.
125
- // 3. Deploys a curl pod to interact with the metrics endpoint.
126
- // 4. Waits for the curl pod to become ready.
127
- // 5. Executes a curl command from the pod to validate the metrics endpoint.
128
- // 6. Cleans up all resources created during the test, such as the ClusterRoleBinding and curl pod.
129
- func TestCatalogdMetricsExportedEndpoint (t * testing.T ) {
130
- var (
131
- token string
132
- curlPod = "curl-metrics"
133
- client = ""
134
- clients = []string {"kubectl" , "oc" }
135
- )
136
+ func waitForPodReady (t * testing.T , client , namespace , podName string ) {
137
+ t .Logf ("Waiting for pod %s to appear before checking readiness..." , podName )
138
+ maxRetries := 10
139
+ delay := time .Second * 3
136
140
137
- t . Log ( "Looking for k8s client" )
138
- for _ , c := range clients {
139
- // Would prefer to use `command -v`, but even that may not be installed!
140
- err := exec . Command ( c , "version" , "--client" ). Run ()
141
+ // Ensure the pod exists before waiting for readiness
142
+ for i := 0 ; i < maxRetries ; i ++ {
143
+ cmd := exec . Command ( client , "get" , "pod" , podName , "-n" , namespace )
144
+ _ , err := cmd . CombinedOutput ()
141
145
if err == nil {
142
- client = c
146
+ t . Logf ( "Pod %s found, proceeding to wait for readiness..." , podName )
143
147
break
144
148
}
149
+ t .Logf ("Pod %s not found yet, retrying... (%d/%d)" , podName , i + 1 , maxRetries )
150
+ time .Sleep (delay )
145
151
}
146
- if client == "" {
147
- t .Fatal ("k8s client not found" )
148
- }
149
- t .Logf ("Using %q as k8s client" , client )
150
152
151
- t .Log ("Determining catalogd namespace" )
152
- cmd := exec .Command (client , "get" , "pods" , "--all-namespaces" , "--selector=control-plane=catalogd-controller-manager" , "--output=jsonpath={.items[0].metadata.namespace}" )
153
+ // Now wait for the pod to become ready
154
+ t .Logf ("Waiting for pod %s to be ready..." , podName )
155
+ cmd := exec .Command (client , "wait" , "--for=condition=Ready" , "pod" , podName , "-n" , namespace , "--timeout=120s" )
153
156
output , err := cmd .CombinedOutput ()
154
- require .NoError (t , err , "Error creating determining catalogd namespace: %s" , string (output ))
155
- namespace := string (output )
156
- if namespace == "" {
157
- t .Fatal ("No catalogd namespace found" )
158
- }
159
- t .Logf ("Using %q as catalogd namespace" , namespace )
160
-
161
- t .Log ("Creating ClusterRoleBinding for metrics access" )
162
- cmd = exec .Command (client , "create" , "clusterrolebinding" , "catalogd-metrics-binding" ,
163
- "--clusterrole=catalogd-metrics-reader" ,
164
- "--serviceaccount=" + namespace + ":catalogd-controller-manager" )
165
- output , err = cmd .CombinedOutput ()
166
- require .NoError (t , err , "Error creating ClusterRoleBinding: %s" , string (output ))
167
-
168
- defer func () {
169
- t .Log ("Cleaning up ClusterRoleBinding" )
170
- _ = exec .Command (client , "delete" , "clusterrolebinding" , "catalogd-metrics-binding" , "--ignore-not-found=true" ).Run ()
171
- }()
172
-
173
- t .Log ("Creating service account token for authentication" )
174
- tokenCmd := exec .Command (client , "create" , "token" , "catalogd-controller-manager" , "-n" , namespace )
175
- tokenOutput , tokenCombinedOutput , err := stdoutAndCombined (tokenCmd )
176
- require .NoError (t , err , "Error creating token: %s" , string (tokenCombinedOutput ))
177
- token = string (bytes .TrimSpace (tokenOutput ))
178
-
179
- t .Log ("Creating a pod to run curl commands" )
180
- cmd = exec .Command (client , "run" , curlPod ,
181
- "--image=curlimages/curl:7.87.0" , "-n" , namespace ,
182
- "--restart=Never" ,
183
- "--overrides" , `{
184
- "spec": {
185
- "containers": [{
186
- "name": "curl",
187
- "image": "curlimages/curl:7.87.0",
188
- "command": ["sh", "-c", "sleep 3600"],
189
- "securityContext": {
190
- "allowPrivilegeEscalation": false,
191
- "capabilities": {
192
- "drop": ["ALL"]
193
- },
194
- "runAsNonRoot": true,
195
- "runAsUser": 1000,
196
- "seccompProfile": {
197
- "type": "RuntimeDefault"
198
- }
199
- }
200
- }],
201
- "serviceAccountName": "catalogd-controller-manager"
202
- }
203
- }` )
204
- output , err = cmd .CombinedOutput ()
205
- require .NoError (t , err , "Error creating curl pod: %s" , string (output ))
206
-
207
- defer func () {
208
- t .Log ("Cleaning up curl pod" )
209
- _ = exec .Command (client , "delete" , "pod" , curlPod , "-n" , namespace , "--ignore-not-found=true" ).Run ()
210
- }()
211
-
212
- t .Log ("Waiting for the curl pod to become ready" )
213
- waitCmd := exec .Command (client , "wait" , "--for=condition=Ready" , "pod" , curlPod , "-n" , namespace , "--timeout=60s" )
214
- waitOutput , waitErr := waitCmd .CombinedOutput ()
215
- require .NoError (t , waitErr , "Error waiting for curl pod to be ready: %s" , string (waitOutput ))
157
+ require .NoError (t , err , "Error waiting for pod to be ready: %s" , string (output ))
158
+ }
216
159
160
+ func validateMetricsEndpoint (t * testing.T , client , namespace , podName , token , metricsURL string ) {
217
161
t .Log ("Validating the metrics endpoint" )
218
- metricsURL := "https://catalogd-service." + namespace + ".svc.cluster.local:7443/metrics"
219
- curlCmd := exec .Command (client , "exec" , curlPod , "-n" , namespace , "--" ,
162
+ cmd := exec .Command (client , "exec" , podName , "-n" , namespace , "--" ,
220
163
"curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , metricsURL )
221
- output , err = curlCmd .CombinedOutput ()
164
+ output , err := cmd .CombinedOutput ()
222
165
require .NoError (t , err , "Error calling metrics endpoint: %s" , string (output ))
223
166
require .Contains (t , string (output ), "200 OK" , "Metrics endpoint did not return 200 OK" )
224
167
}
225
168
226
169
func stdoutAndCombined (cmd * exec.Cmd ) ([]byte , []byte , error ) {
227
- var outOnly bytes.Buffer
228
- var outAndErr bytes.Buffer
170
+ var outOnly , outAndErr bytes.Buffer
229
171
allWriter := io .MultiWriter (& outOnly , & outAndErr )
230
- cmd .Stderr = & outAndErr
231
172
cmd .Stdout = allWriter
173
+ cmd .Stderr = & outAndErr
232
174
err := cmd .Run ()
233
175
return outOnly .Bytes (), outAndErr .Bytes (), err
234
176
}
0 commit comments