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.
113package e2e
214
315import (
416 "bytes"
517 "io"
618 "os/exec"
719 "testing"
20+ "time"
821
922 "github.com/stretchr/testify/require"
23+
24+ "github.com/operator-framework/operator-controller/test/utils"
1025)
1126
12- // nolint:gosec
1327// 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.
2228func 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" ,
2836 )
2937
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" )
4364
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}" )
4673 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 ))
4977 if namespace == "" {
50- t .Fatal ("No operator-controller namespace found" )
78+ t .Fatal ("No namespace found for selector " + selector )
5179 }
52- t .Logf ("Using %q as operator-controller namespace" , namespace )
80+ return namespace
81+ }
5382
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 ()
5989 require .NoError (t , err , "Error creating ClusterRoleBinding: %s" , string (output ))
6090
6191 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 ()
6494 }()
95+ }
6596
97+ func createServiceAccountToken (t * testing.T , client , namespace , serviceAccount string ) string {
6698 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 )
69101 require .NoError (t , err , "Error creating token: %s" , string (tokenCombinedOutput ))
70- token = string (bytes .TrimSpace (tokenOutput ))
102+ return string (bytes .TrimSpace (tokenOutput ))
103+ }
71104
105+ func createCurlPod (t * testing.T , client , namespace , podName , serviceAccount string ) {
72106 t .Log ("Creating curl pod to validate the metrics endpoint" )
73- cmd = exec .Command (client , "run" , curlPod ,
107+ cmd : = exec .Command (client , "run" , podName ,
74108 "--image=curlimages/curl:7.87.0" , "-n" , namespace ,
75109 "--restart=Never" ,
76110 "--overrides" , `{
@@ -81,154 +115,62 @@ func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
81115 "command": ["sh", "-c", "sleep 3600"],
82116 "securityContext": {
83117 "allowPrivilegeEscalation": false,
84- "capabilities": {
85- "drop": ["ALL"]
86- },
118+ "capabilities": {"drop": ["ALL"]},
87119 "runAsNonRoot": true,
88120 "runAsUser": 1000,
89- "seccompProfile": {
90- "type": "RuntimeDefault"
91- }
121+ "seccompProfile": {"type": "RuntimeDefault"}
92122 }
93123 }],
94- "serviceAccountName": "operator-controller-controller-manager "
124+ "serviceAccountName": "` + serviceAccount + ` "
95125 }
96126 }` )
97- output , err = cmd .CombinedOutput ()
127+ output , err : = cmd .CombinedOutput ()
98128 require .NoError (t , err , "Error creating curl pod: %s" , string (output ))
99129
100130 defer func () {
101131 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 ()
103133 }()
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" )
117134}
118135
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
136140
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 ()
141145 if err == nil {
142- client = c
146+ t . Logf ( "Pod %s found, proceeding to wait for readiness..." , podName )
143147 break
144148 }
149+ t .Logf ("Pod %s not found yet, retrying... (%d/%d)" , podName , i + 1 , maxRetries )
150+ time .Sleep (delay )
145151 }
146- if client == "" {
147- t .Fatal ("k8s client not found" )
148- }
149- t .Logf ("Using %q as k8s client" , client )
150152
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" )
153156 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+ }
216159
160+ func validateMetricsEndpoint (t * testing.T , client , namespace , podName , token , metricsURL string ) {
217161 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 , "--" ,
220163 "curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , metricsURL )
221- output , err = curlCmd .CombinedOutput ()
164+ output , err := cmd .CombinedOutput ()
222165 require .NoError (t , err , "Error calling metrics endpoint: %s" , string (output ))
223166 require .Contains (t , string (output ), "200 OK" , "Metrics endpoint did not return 200 OK" )
224167}
225168
226169func stdoutAndCombined (cmd * exec.Cmd ) ([]byte , []byte , error ) {
227- var outOnly bytes.Buffer
228- var outAndErr bytes.Buffer
170+ var outOnly , outAndErr bytes.Buffer
229171 allWriter := io .MultiWriter (& outOnly , & outAndErr )
230- cmd .Stderr = & outAndErr
231172 cmd .Stdout = allWriter
173+ cmd .Stderr = & outAndErr
232174 err := cmd .Run ()
233175 return outOnly .Bytes (), outAndErr .Bytes (), err
234176}
0 commit comments