Skip to content

Commit e1579a6

Browse files
(cleanup): (cleanup): Refactor metrics endpoint tests by extracting shared helpers
Refactored the metrics endpoint tests by introducing reusable helper functions. This reduces duplication and improves maintainability across both tests.
1 parent 591c73c commit e1579a6

File tree

2 files changed

+84
-155
lines changed

2 files changed

+84
-155
lines changed

test/e2e/metrics_test.go

Lines changed: 61 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//nolint:gosec
12
package e2e
23

34
import (
@@ -7,9 +8,10 @@ import (
78
"testing"
89

910
"github.com/stretchr/testify/require"
11+
12+
"github.com/operator-framework/operator-controller/test/utils"
1013
)
1114

12-
// nolint:gosec
1315
// TestOperatorControllerMetricsExportedEndpoint verifies that the metrics endpoint for the operator controller
1416
// is exported correctly and accessible by authorized users through RBAC and a ServiceAccount token.
1517
// The test performs the following steps:
@@ -20,103 +22,15 @@ import (
2022
// 5. Executes a curl command from the pod to validate the metrics endpoint.
2123
// 6. Cleans up all resources created during the test, such as the ClusterRoleBinding and curl pod.
2224
func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
23-
var (
24-
token string
25-
curlPod = "curl-metrics"
26-
client = ""
27-
clients = []string{"kubectl", "oc"}
28-
)
29-
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)
43-
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}")
46-
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,
75-
"--restart=Never",
76-
"--overrides", `{
77-
"spec": {
78-
"containers": [{
79-
"name": "curl",
80-
"image": "curlimages/curl:7.87.0",
81-
"command": ["sh", "-c", "sleep 3600"],
82-
"securityContext": {
83-
"allowPrivilegeEscalation": false,
84-
"capabilities": {
85-
"drop": ["ALL"]
86-
},
87-
"runAsNonRoot": true,
88-
"runAsUser": 1000,
89-
"seccompProfile": {
90-
"type": "RuntimeDefault"
91-
}
92-
}
93-
}],
94-
"serviceAccountName": "operator-controller-controller-manager"
95-
}
96-
}`)
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-
}()
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")
25+
client := utils.FindK8sClient(t)
26+
namespace := getNamespace(t, client, "control-plane=operator-controller-controller-manager")
27+
createClusterRoleBinding(t, client, "operator-controller-metrics-binding", "operator-controller-metrics-reader", namespace, "operator-controller-controller-manager")
28+
token := createServiceAccountToken(t, client, namespace, "operator-controller-controller-manager")
29+
createCurlPod(t, client, namespace, "curl-metrics", "operator-controller-controller-manager")
30+
waitForPodReady(t, client, namespace, "curl-metrics")
31+
validateMetricsEndpoint(t, client, namespace, "curl-metrics", token, "https://operator-controller-service."+namespace+".svc.cluster.local:8443/metrics")
11732
}
11833

119-
// nolint:gosec
12034
// TestCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for the catalogd
12135
// is exported correctly and accessible by authorized users through RBAC and a ServiceAccount token.
12236
// The test performs the following steps:
@@ -127,57 +41,52 @@ func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
12741
// 5. Executes a curl command from the pod to validate the metrics endpoint.
12842
// 6. Cleans up all resources created during the test, such as the ClusterRoleBinding and curl pod.
12943
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)
44+
client := utils.FindK8sClient(t)
45+
namespace := getNamespace(t, client, "control-plane=catalogd-controller-manager")
46+
createClusterRoleBinding(t, client, "catalogd-metrics-binding", "catalogd-metrics-reader", namespace, "catalogd-controller-manager")
47+
token := createServiceAccountToken(t, client, namespace, "catalogd-controller-manager")
48+
createCurlPod(t, client, namespace, "curl-metrics", "catalogd-controller-manager")
49+
waitForPodReady(t, client, namespace, "curl-metrics")
50+
validateMetricsEndpoint(t, client, namespace, "curl-metrics", token, "https://catalogd-service."+namespace+".svc.cluster.local:7443/metrics")
51+
}
15052

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}")
53+
func getNamespace(t *testing.T, client, selector string) string {
54+
cmd := exec.Command(client, "get", "pods", "--all-namespaces", "--selector="+selector, "--output=jsonpath={.items[0].metadata.namespace}")
15355
output, err := cmd.CombinedOutput()
154-
require.NoError(t, err, "Error creating determining catalogd namespace: %s", string(output))
155-
namespace := string(output)
56+
require.NoError(t, err, "Error determining namespace: %s", string(output))
57+
58+
namespace := string(bytes.TrimSpace(output))
15659
if namespace == "" {
157-
t.Fatal("No catalogd namespace found")
60+
t.Fatal("No namespace found for selector " + selector)
15861
}
159-
t.Logf("Using %q as catalogd namespace", namespace)
62+
return namespace
63+
}
16064

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()
65+
func createClusterRoleBinding(t *testing.T, client, name, clusterRole, namespace, serviceAccount string) {
66+
t.Logf("Creating ClusterRoleBinding %s", name)
67+
cmd := exec.Command(client, "create", "clusterrolebinding", name,
68+
"--clusterrole="+clusterRole,
69+
"--serviceaccount="+namespace+":"+serviceAccount)
70+
output, err := cmd.CombinedOutput()
16671
require.NoError(t, err, "Error creating ClusterRoleBinding: %s", string(output))
16772

16873
defer func() {
169-
t.Log("Cleaning up ClusterRoleBinding")
170-
_ = exec.Command(client, "delete", "clusterrolebinding", "catalogd-metrics-binding", "--ignore-not-found=true").Run()
74+
t.Logf("Cleaning up ClusterRoleBinding %s", name)
75+
_ = exec.Command(client, "delete", "clusterrolebinding", name, "--ignore-not-found=true").Run()
17176
}()
77+
}
17278

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)
79+
func createServiceAccountToken(t *testing.T, client, namespace, serviceAccount string) string {
80+
t.Log("Generating ServiceAccount token")
81+
cmd := exec.Command(client, "create", "token", serviceAccount, "-n", namespace)
82+
tokenOutput, tokenCombinedOutput, err := stdoutAndCombined(cmd)
17683
require.NoError(t, err, "Error creating token: %s", string(tokenCombinedOutput))
177-
token = string(bytes.TrimSpace(tokenOutput))
84+
return string(bytes.TrimSpace(tokenOutput))
85+
}
17886

179-
t.Log("Creating a pod to run curl commands")
180-
cmd = exec.Command(client, "run", curlPod,
87+
func createCurlPod(t *testing.T, client, namespace, podName, serviceAccount string) {
88+
t.Log("Creating curl pod to validate the metrics endpoint")
89+
cmd := exec.Command(client, "run", podName,
18190
"--image=curlimages/curl:7.87.0", "-n", namespace,
18291
"--restart=Never",
18392
"--overrides", `{
@@ -188,47 +97,44 @@ func TestCatalogdMetricsExportedEndpoint(t *testing.T) {
18897
"command": ["sh", "-c", "sleep 3600"],
18998
"securityContext": {
19099
"allowPrivilegeEscalation": false,
191-
"capabilities": {
192-
"drop": ["ALL"]
193-
},
100+
"capabilities": {"drop": ["ALL"]},
194101
"runAsNonRoot": true,
195102
"runAsUser": 1000,
196-
"seccompProfile": {
197-
"type": "RuntimeDefault"
198-
}
103+
"seccompProfile": {"type": "RuntimeDefault"}
199104
}
200105
}],
201-
"serviceAccountName": "catalogd-controller-manager"
106+
"serviceAccountName": "`+serviceAccount+`"
202107
}
203108
}`)
204-
output, err = cmd.CombinedOutput()
109+
output, err := cmd.CombinedOutput()
205110
require.NoError(t, err, "Error creating curl pod: %s", string(output))
206111

207112
defer func() {
208113
t.Log("Cleaning up curl pod")
209-
_ = exec.Command(client, "delete", "pod", curlPod, "-n", namespace, "--ignore-not-found=true").Run()
114+
_ = exec.Command(client, "delete", "pod", podName, "-n", namespace, "--ignore-not-found=true").Run()
210115
}()
116+
}
211117

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))
118+
func waitForPodReady(t *testing.T, client, namespace, podName string) {
119+
t.Log("Waiting for the curl pod to be ready")
120+
cmd := exec.Command(client, "wait", "--for=condition=Ready", "pod", podName, "-n", namespace, "--timeout=60s")
121+
output, err := cmd.CombinedOutput()
122+
require.NoError(t, err, "Error waiting for curl pod to be ready: %s", string(output))
123+
}
216124

125+
func validateMetricsEndpoint(t *testing.T, client, namespace, podName, token, metricsURL string) {
217126
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, "--",
127+
cmd := exec.Command(client, "exec", podName, "-n", namespace, "--",
220128
"curl", "-v", "-k", "-H", "Authorization: Bearer "+token, metricsURL)
221-
output, err = curlCmd.CombinedOutput()
129+
output, err := cmd.CombinedOutput()
222130
require.NoError(t, err, "Error calling metrics endpoint: %s", string(output))
223131
require.Contains(t, string(output), "200 OK", "Metrics endpoint did not return 200 OK")
224132
}
225133

226134
func stdoutAndCombined(cmd *exec.Cmd) ([]byte, []byte, error) {
227-
var outOnly bytes.Buffer
228-
var outAndErr bytes.Buffer
229-
allWriter := io.MultiWriter(&outOnly, &outAndErr)
135+
var outOnly, outAndErr bytes.Buffer
136+
cmd.Stdout = io.MultiWriter(&outOnly, &outAndErr)
230137
cmd.Stderr = &outAndErr
231-
cmd.Stdout = allWriter
232138
err := cmd.Run()
233139
return outOnly.Bytes(), outAndErr.Bytes(), err
234140
}

test/utils/utils.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package utils
2+
3+
import (
4+
"os/exec"
5+
"testing"
6+
)
7+
8+
// FindK8sClient returns the first available Kubernetes CLI client from the system,
9+
// It checks for the existence of each client by running `version --client`.
10+
// If no suitable client is found, the function terminates the test with a failure.
11+
func FindK8sClient(t *testing.T) string {
12+
t.Logf("Finding kubectl client")
13+
clients := []string{"kubectl", "oc"}
14+
for _, c := range clients {
15+
// Would prefer to use `command -v`, but even that may not be installed!
16+
if err := exec.Command(c, "version", "--client").Run(); err == nil {
17+
t.Logf("Using %q as k8s client", c)
18+
return c
19+
}
20+
}
21+
t.Fatal("k8s client not found")
22+
return "" // unreachable but required
23+
}

0 commit comments

Comments
 (0)