1
+ // nolint:gosec
2
+
3
+ // Package e2e contains end-to-end tests to verify that the metrics endpoints
4
+ // for both components. Metrics are exported and accessible by authorized users through
5
+ // RBAC and ServiceAccount tokens.
6
+ //
7
+ // These tests perform the following steps:
8
+ // 1. Create a ClusterRoleBinding to grant necessary permissions for accessing metrics.
9
+ // 2. Generate a ServiceAccount token for authentication.
10
+ // 3. Deploy a curl pod to interact with the metrics endpoint.
11
+ // 4. Wait for the curl pod to become ready.
12
+ // 5. Execute a curl command from the pod to validate the metrics endpoint.
13
+ // 6. Clean up all resources created during the test, such as the ClusterRoleBinding and curl pod.
1
14
package e2e
2
15
3
16
import (
4
17
"bytes"
5
18
"io"
6
19
"os/exec"
20
+ "strings"
7
21
"testing"
8
22
9
23
"github.com/stretchr/testify/require"
24
+
25
+ "github.com/operator-framework/operator-controller/test/utils"
10
26
)
11
27
12
- // nolint:gosec
13
28
// 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
29
func TestOperatorControllerMetricsExportedEndpoint (t * testing.T ) {
23
- var (
24
- token string
25
- curlPod = "curl-metrics"
26
- client = ""
27
- clients = []string {"kubectl" , "oc" }
30
+ client := utils .FindK8sClient (t )
31
+ config := NewMetricsTestConfig (
32
+ t , client ,
33
+ "control-plane=operator-controller-controller-manager" ,
34
+ "operator-controller-metrics-reader" ,
35
+ "operator-controller-metrics-binding" ,
36
+ "operator-controller-controller-manager" ,
37
+ "oper-curl-metrics" ,
38
+ "https://operator-controller-service.NAMESPACE.svc.cluster.local:8443/metrics" ,
28
39
)
29
40
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
+ config .createMetricsClusterRoleBinding ()
42
+ token := config .getServiceAccountToken ()
43
+ config .createCurlMetricsPod ()
44
+ config .validate (token )
45
+ defer config .cleanup ()
46
+ }
47
+
48
+ // TestCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for catalogd
49
+ func TestCatalogdMetricsExportedEndpoint (t * testing.T ) {
50
+ client := utils .FindK8sClient (t )
51
+ config := NewMetricsTestConfig (
52
+ t , client ,
53
+ "control-plane=catalogd-controller-manager" ,
54
+ "catalogd-metrics-reader" ,
55
+ "catalogd-metrics-binding" ,
56
+ "catalogd-controller-manager" ,
57
+ "catalogd-curl-metrics" ,
58
+ "https://catalogd-service.NAMESPACE.svc.cluster.local:7443/metrics" ,
59
+ )
60
+
61
+ config .createMetricsClusterRoleBinding ()
62
+ token := config .getServiceAccountToken ()
63
+ config .createCurlMetricsPod ()
64
+ config .validate (token )
65
+ defer config .cleanup ()
66
+ }
67
+
68
+ // MetricsTestConfig holds the necessary configurations for testing metrics endpoints.
69
+ type MetricsTestConfig struct {
70
+ t * testing.T
71
+ client string
72
+ namespace string
73
+ clusterRole string
74
+ clusterBinding string
75
+ serviceAccount string
76
+ curlPodName string
77
+ metricsURL string
78
+ }
79
+
80
+ // NewMetricsTestConfig initializes a new MetricsTestConfig.
81
+ func NewMetricsTestConfig (t * testing.T , client , selector , clusterRole , clusterBinding , serviceAccount , curlPodName , metricsURL string ) * MetricsTestConfig {
82
+ namespace := getComponentNamespace (t , client , selector )
83
+ metricsURL = strings .ReplaceAll (metricsURL , "NAMESPACE" , namespace )
84
+
85
+ return & MetricsTestConfig {
86
+ t : t ,
87
+ client : client ,
88
+ namespace : namespace ,
89
+ clusterRole : clusterRole ,
90
+ clusterBinding : clusterBinding ,
91
+ serviceAccount : serviceAccount ,
92
+ curlPodName : curlPodName ,
93
+ metricsURL : metricsURL ,
41
94
}
42
- t . Logf ( "Using %q as k8s client" , client )
95
+ }
43
96
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}" )
97
+ func (c * MetricsTestConfig ) createMetricsClusterRoleBinding () {
98
+ c .t .Logf ("Creating ClusterRoleBinding %s in namespace %s" , c .clusterBinding , c .namespace )
99
+ cmd := exec .Command (c .client , "create" , "clusterrolebinding" , c .clusterBinding ,
100
+ "--clusterrole=" + c .clusterRole ,
101
+ "--serviceaccount=" + c .namespace + ":" + c .serviceAccount )
46
102
output , err := cmd .CombinedOutput ()
47
- require .NoError (t , err , "Error creating determining operator-controller namespace: %s" , string (output ))
48
- namespace := string (output )
49
- if namespace == "" {
50
- t .Fatal ("No operator-controller namespace found" )
51
- }
52
- t .Logf ("Using %q as operator-controller namespace" , namespace )
53
-
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 ()
59
- require .NoError (t , err , "Error creating ClusterRoleBinding: %s" , string (output ))
60
-
61
- defer func () {
62
- t .Log ("Cleaning up ClusterRoleBinding" )
63
- _ = exec .Command (client , "delete" , "clusterrolebinding" , "operator-controller-metrics-binding" , "--ignore-not-found=true" ).Run ()
64
- }()
65
-
66
- t .Log ("Generating ServiceAccount token" )
67
- tokenCmd := exec .Command (client , "create" , "token" , "operator-controller-controller-manager" , "-n" , namespace )
68
- tokenOutput , tokenCombinedOutput , err := stdoutAndCombined (tokenCmd )
69
- require .NoError (t , err , "Error creating token: %s" , string (tokenCombinedOutput ))
70
- token = string (bytes .TrimSpace (tokenOutput ))
71
-
72
- t .Log ("Creating curl pod to validate the metrics endpoint" )
73
- cmd = exec .Command (client , "run" , curlPod ,
74
- "--image=curlimages/curl:7.87.0" , "-n" , namespace ,
103
+ require .NoError (c .t , err , "Error creating ClusterRoleBinding: %s" , string (output ))
104
+ }
105
+
106
+ func (c * MetricsTestConfig ) getServiceAccountToken () string {
107
+ c .t .Logf ("Generating ServiceAccount token at namespace %s" , c .namespace )
108
+ cmd := exec .Command (c .client , "create" , "token" , c .serviceAccount , "-n" , c .namespace )
109
+ tokenOutput , tokenCombinedOutput , err := stdoutAndCombined (cmd )
110
+ require .NoError (c .t , err , "Error creating token: %s" , string (tokenCombinedOutput ))
111
+ return string (bytes .TrimSpace (tokenOutput ))
112
+ }
113
+
114
+ func (c * MetricsTestConfig ) createCurlMetricsPod () {
115
+ c .t .Logf ("Creating curl pod (%s/%s) to validate the metrics endpoint" , c .namespace , c .curlPodName )
116
+ cmd := exec .Command (c .client , "run" , c .curlPodName ,
117
+ "--image=curlimages/curl:7.87.0" , "-n" , c .namespace ,
75
118
"--restart=Never" ,
76
119
"--overrides" , `{
77
120
"spec": {
@@ -81,154 +124,57 @@ func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
81
124
"command": ["sh", "-c", "sleep 3600"],
82
125
"securityContext": {
83
126
"allowPrivilegeEscalation": false,
84
- "capabilities": {
85
- "drop": ["ALL"]
86
- },
127
+ "capabilities": {"drop": ["ALL"]},
87
128
"runAsNonRoot": true,
88
129
"runAsUser": 1000,
89
- "seccompProfile": {
90
- "type": "RuntimeDefault"
91
- }
130
+ "seccompProfile": {"type": "RuntimeDefault"}
92
131
}
93
132
}],
94
- "serviceAccountName": "operator-controller-controller-manager "
133
+ "serviceAccountName": "` + c . serviceAccount + ` "
95
134
}
96
135
}` )
97
- output , err = cmd .CombinedOutput ()
98
- require .NoError (t , err , "Error creating curl pod: %s" , string (output ))
99
-
100
- defer func () {
101
- t .Log ("Cleaning up curl pod" )
102
- _ = exec .Command (client , "delete" , "pod" , curlPod , "-n" , namespace , "--ignore-not-found=true" ).Run ()
103
- }()
136
+ output , err := cmd .CombinedOutput ()
137
+ require .NoError (c .t , err , "Error creating curl pod: %s" , string (output ))
138
+ }
104
139
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" )
140
+ func (c * MetricsTestConfig ) validate (token string ) {
141
+ c .t .Log ("Waiting for the curl pod to be ready" )
142
+ waitCmd := exec .Command (c .client , "wait" , "--for=condition=Ready" , "pod" , c .curlPodName , "-n" , c .namespace , "--timeout=60s" )
107
143
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" )
144
+ require .NoError (c .t , waitErr , "Error waiting for curl pod to be ready: %s" , string (waitOutput ))
145
+
146
+ c .t .Log ("Validating the metrics endpoint" )
147
+ curlCmd := exec .Command (c .client , "exec" , c .curlPodName , "-n" , c .namespace , "--" ,
148
+ "curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , c .metricsURL )
149
+ output , err := curlCmd .CombinedOutput ()
150
+ require .NoError (c .t , err , "Error calling metrics endpoint: %s" , string (output ))
151
+ require .Contains (c .t , string (output ), "200 OK" , "Metrics endpoint did not return 200 OK" )
117
152
}
118
153
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
-
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
- if err == nil {
142
- client = c
143
- break
144
- }
145
- }
146
- if client == "" {
147
- t .Fatal ("k8s client not found" )
148
- }
149
- t .Logf ("Using %q as k8s client" , client )
154
+ func (c * MetricsTestConfig ) cleanup () {
155
+ c .t .Log ("Cleaning up resources" )
156
+ _ = exec .Command (c .client , "delete" , "clusterrolebinding" , c .clusterBinding , "--ignore-not-found=true" ).Run ()
157
+ _ = exec .Command (c .client , "delete" , "pod" , c .curlPodName , "-n" , c .namespace , "--ignore-not-found=true" ).Run ()
158
+ }
150
159
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}" )
160
+ // getComponentNamespace returns the namespace where operator-controller or catalogd is running
161
+ func getComponentNamespace (t * testing.T , client , selector string ) string {
162
+ cmd := exec .Command (client , "get" , "pods" , "--all-namespaces" , "--selector=" + selector , "--output=jsonpath={.items[0].metadata.namespace}" )
153
163
output , err := cmd .CombinedOutput ()
154
- require .NoError (t , err , "Error creating determining catalogd namespace: %s" , string (output ))
155
- namespace := string (output )
164
+ require .NoError (t , err , "Error determining namespace: %s" , string (output ))
165
+
166
+ namespace := string (bytes .TrimSpace (output ))
156
167
if namespace == "" {
157
- t .Fatal ("No catalogd namespace found" )
168
+ t .Fatal ("No namespace found for selector " + selector )
158
169
}
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 ))
216
-
217
- 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 , "--" ,
220
- "curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , metricsURL )
221
- output , err = curlCmd .CombinedOutput ()
222
- require .NoError (t , err , "Error calling metrics endpoint: %s" , string (output ))
223
- require .Contains (t , string (output ), "200 OK" , "Metrics endpoint did not return 200 OK" )
170
+ return namespace
224
171
}
225
172
226
173
func stdoutAndCombined (cmd * exec.Cmd ) ([]byte , []byte , error ) {
227
- var outOnly bytes.Buffer
228
- var outAndErr bytes.Buffer
174
+ var outOnly , outAndErr bytes.Buffer
229
175
allWriter := io .MultiWriter (& outOnly , & outAndErr )
230
- cmd .Stderr = & outAndErr
231
176
cmd .Stdout = allWriter
177
+ cmd .Stderr = & outAndErr
232
178
err := cmd .Run ()
233
179
return outOnly .Bytes (), outAndErr .Bytes (), err
234
180
}
0 commit comments