@@ -19,50 +19,57 @@ import (
19
19
"fmt"
20
20
"io"
21
21
"os/exec"
22
- "strings"
23
22
"testing"
24
23
"time"
25
24
26
25
"github.com/stretchr/testify/require"
26
+ "k8s.io/apimachinery/pkg/util/rand"
27
27
28
28
"github.com/operator-framework/operator-controller/test/utils"
29
29
)
30
30
31
31
// TestOperatorControllerMetricsExportedEndpoint verifies that the metrics endpoint for the operator controller
32
32
func TestOperatorControllerMetricsExportedEndpoint (t * testing.T ) {
33
33
client := utils .FindK8sClient (t )
34
+ curlNamespace := createRandomNamespace (t , client )
35
+ componentNamespace := getComponentNamespace (t , client , "control-plane=operator-controller-controller-manager" )
36
+ metricsURL := fmt .Sprintf ("https://operator-controller-service.%s.svc.cluster.local:8443/metrics" , componentNamespace )
37
+
34
38
config := NewMetricsTestConfig (
35
- t , client ,
36
- "control-plane=operator-controller-controller-manager" ,
39
+ client ,
40
+ curlNamespace ,
37
41
"operator-controller-metrics-reader" ,
38
42
"operator-controller-metrics-binding" ,
39
- "operator-controller-controller-manager " ,
43
+ "operator-controller-metrics-reader " ,
40
44
"oper-curl-metrics" ,
41
- "https://operator-controller-service.NAMESPACE.svc.cluster.local:8443/metrics" ,
45
+ metricsURL ,
42
46
)
43
47
44
- config .run ()
48
+ config .run (t )
45
49
}
46
50
47
51
// TestCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for catalogd
48
52
func TestCatalogdMetricsExportedEndpoint (t * testing.T ) {
49
53
client := utils .FindK8sClient (t )
54
+ curlNamespace := createRandomNamespace (t , client )
55
+ componentNamespace := getComponentNamespace (t , client , "control-plane=catalogd-controller-manager" )
56
+ metricsURL := fmt .Sprintf ("https://catalogd-service.%s.svc.cluster.local:7443/metrics" , componentNamespace )
57
+
50
58
config := NewMetricsTestConfig (
51
- t , client ,
52
- "control-plane=catalogd-controller-manager" ,
59
+ client ,
60
+ curlNamespace ,
53
61
"catalogd-metrics-reader" ,
54
62
"catalogd-metrics-binding" ,
55
- "catalogd-controller-manager " ,
63
+ "catalogd-metrics-reader " ,
56
64
"catalogd-curl-metrics" ,
57
- "https://catalogd-service.NAMESPACE.svc.cluster.local:7443/metrics" ,
65
+ metricsURL ,
58
66
)
59
67
60
- config .run ()
68
+ config .run (t )
61
69
}
62
70
63
71
// MetricsTestConfig holds the necessary configurations for testing metrics endpoints.
64
72
type MetricsTestConfig struct {
65
- t * testing.T
66
73
client string
67
74
namespace string
68
75
clusterRole string
@@ -73,12 +80,8 @@ type MetricsTestConfig struct {
73
80
}
74
81
75
82
// NewMetricsTestConfig initializes a new MetricsTestConfig.
76
- func NewMetricsTestConfig (t * testing.T , client , selector , clusterRole , clusterBinding , serviceAccount , curlPodName , metricsURL string ) * MetricsTestConfig {
77
- namespace := getComponentNamespace (t , client , selector )
78
- metricsURL = strings .ReplaceAll (metricsURL , "NAMESPACE" , namespace )
79
-
83
+ func NewMetricsTestConfig (client , namespace , clusterRole , clusterBinding , serviceAccount , curlPodName , metricsURL string ) * MetricsTestConfig {
80
84
return & MetricsTestConfig {
81
- t : t ,
82
85
client : client ,
83
86
namespace : namespace ,
84
87
clusterRole : clusterRole ,
@@ -90,38 +93,44 @@ func NewMetricsTestConfig(t *testing.T, client, selector, clusterRole, clusterBi
90
93
}
91
94
92
95
// run will execute all steps of those tests
93
- func (c * MetricsTestConfig ) run () {
94
- c .createMetricsClusterRoleBinding ()
95
- token := c .getServiceAccountToken ()
96
- c .createCurlMetricsPod ()
97
- c .validate (token )
98
- defer c .cleanup ()
96
+ func (c * MetricsTestConfig ) run (t * testing.T ) {
97
+ defer c .cleanup (t )
98
+
99
+ c .createMetricsClusterRoleBinding (t )
100
+ token := c .getServiceAccountToken (t )
101
+ c .createCurlMetricsPod (t )
102
+ c .validate (t , token )
99
103
}
100
104
101
105
// createMetricsClusterRoleBinding to binding and expose the metrics
102
- func (c * MetricsTestConfig ) createMetricsClusterRoleBinding () {
103
- c . t .Logf ("Creating ClusterRoleBinding %s in namespace %s" , c .clusterBinding , c .namespace )
106
+ func (c * MetricsTestConfig ) createMetricsClusterRoleBinding (t * testing. T ) {
107
+ t .Logf ("Creating ClusterRoleBinding %s for %s in namespace %s" , c .clusterBinding , c . serviceAccount , c .namespace )
104
108
cmd := exec .Command (c .client , "create" , "clusterrolebinding" , c .clusterBinding ,
105
109
"--clusterrole=" + c .clusterRole ,
106
110
"--serviceaccount=" + c .namespace + ":" + c .serviceAccount )
107
111
output , err := cmd .CombinedOutput ()
108
- require .NoError (c . t , err , "Error creating ClusterRoleBinding: %s" , string (output ))
112
+ require .NoError (t , err , "Error creating ClusterRoleBinding: %s" , string (output ))
109
113
}
110
114
111
115
// getServiceAccountToken return the token requires to have access to the metrics
112
- func (c * MetricsTestConfig ) getServiceAccountToken () string {
113
- c .t .Logf ("Generating ServiceAccount token at namespace %s" , c .namespace )
114
- cmd := exec .Command (c .client , "create" , "token" , c .serviceAccount , "-n" , c .namespace )
116
+ func (c * MetricsTestConfig ) getServiceAccountToken (t * testing.T ) string {
117
+ t .Logf ("Creating ServiceAccount %q in namespace %q" , c .serviceAccount , c .namespace )
118
+ output , err := exec .Command (c .client , "create" , "serviceaccount" , c .serviceAccount , "--namespace=" + c .namespace ).CombinedOutput ()
119
+ require .NoError (t , err , "Error creating service account: %v" , string (output ))
120
+
121
+ t .Logf ("Generating ServiceAccount token for %q in namespace %q" , c .serviceAccount , c .namespace )
122
+ cmd := exec .Command (c .client , "create" , "token" , c .serviceAccount , "--namespace" , c .namespace )
115
123
tokenOutput , tokenCombinedOutput , err := stdoutAndCombined (cmd )
116
- require .NoError (c . t , err , "Error creating token: %s" , string (tokenCombinedOutput ))
124
+ require .NoError (t , err , "Error creating token: %s" , string (tokenCombinedOutput ))
117
125
return string (bytes .TrimSpace (tokenOutput ))
118
126
}
119
127
120
128
// createCurlMetricsPod creates the Pod with curl image to allow check if the metrics are working
121
- func (c * MetricsTestConfig ) createCurlMetricsPod () {
122
- c . t .Logf ("Creating curl pod (%s/%s) to validate the metrics endpoint" , c .namespace , c .curlPodName )
129
+ func (c * MetricsTestConfig ) createCurlMetricsPod (t * testing. T ) {
130
+ t .Logf ("Creating curl pod (%s/%s) to validate the metrics endpoint" , c .namespace , c .curlPodName )
123
131
cmd := exec .Command (c .client , "run" , c .curlPodName ,
124
- "--image=curlimages/curl" , "-n" , c .namespace ,
132
+ "--image=curlimages/curl" ,
133
+ "--namespace" , c .namespace ,
125
134
"--restart=Never" ,
126
135
"--overrides" , `{
127
136
"spec": {
@@ -142,55 +151,66 @@ func (c *MetricsTestConfig) createCurlMetricsPod() {
142
151
}
143
152
}` )
144
153
output , err := cmd .CombinedOutput ()
145
- require .NoError (c . t , err , "Error creating curl pod: %s" , string (output ))
154
+ require .NoError (t , err , "Error creating curl pod: %s" , string (output ))
146
155
}
147
156
148
157
// validate verifies if is possible to access the metrics
149
- func (c * MetricsTestConfig ) validate (token string ) {
150
- c . t .Log ("Waiting for the curl pod to be ready" )
151
- waitCmd := exec .Command (c .client , "wait" , "--for=condition=Ready" , "pod" , c .curlPodName , "-n " , c .namespace , "--timeout=60s" )
158
+ func (c * MetricsTestConfig ) validate (t * testing. T , token string ) {
159
+ t .Log ("Waiting for the curl pod to be ready" )
160
+ waitCmd := exec .Command (c .client , "wait" , "--for=condition=Ready" , "pod" , c .curlPodName , "--namespace " , c .namespace , "--timeout=60s" )
152
161
waitOutput , waitErr := waitCmd .CombinedOutput ()
153
- require .NoError (c . t , waitErr , "Error waiting for curl pod to be ready: %s" , string (waitOutput ))
162
+ require .NoError (t , waitErr , "Error waiting for curl pod to be ready: %s" , string (waitOutput ))
154
163
155
- c . t .Log ("Validating the metrics endpoint" )
156
- curlCmd := exec .Command (c .client , "exec" , c .curlPodName , "-n " , c .namespace , "--" ,
164
+ t .Log ("Validating the metrics endpoint" )
165
+ curlCmd := exec .Command (c .client , "exec" , c .curlPodName , "--namespace " , c .namespace , "--" ,
157
166
"curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , c .metricsURL )
158
167
output , err := curlCmd .CombinedOutput ()
159
- require .NoError (c . t , err , "Error calling metrics endpoint: %s" , string (output ))
160
- require .Contains (c . t , string (output ), "200 OK" , "Metrics endpoint did not return 200 OK" )
168
+ require .NoError (t , err , "Error calling metrics endpoint: %s" , string (output ))
169
+ require .Contains (t , string (output ), "200 OK" , "Metrics endpoint did not return 200 OK" )
161
170
}
162
171
163
172
// cleanup removes the created resources. Uses a context with timeout to prevent hangs.
164
- func (c * MetricsTestConfig ) cleanup () {
165
- c .t .Log ("Cleaning up resources" )
166
- _ = exec .Command (c .client , "delete" , "clusterrolebinding" , c .clusterBinding , "--ignore-not-found=true" , "--force" ).Run ()
167
- _ = exec .Command (c .client , "delete" , "pod" , c .curlPodName , "-n" , c .namespace , "--ignore-not-found=true" , "--force" ).Run ()
173
+ func (c * MetricsTestConfig ) cleanup (t * testing.T ) {
174
+ type objDesc struct {
175
+ resourceName string
176
+ name string
177
+ namespace string
178
+ }
179
+ objects := []objDesc {
180
+ {"clusterrolebinding" , c .clusterBinding , "" },
181
+ {"pod" , c .curlPodName , c .namespace },
182
+ {"serviceaccount" , c .serviceAccount , c .namespace },
183
+ {"namespace" , c .namespace , "" },
184
+ }
185
+
186
+ t .Log ("Cleaning up resources" )
187
+ for _ , obj := range objects {
188
+ args := []string {"delete" , obj .resourceName , obj .name , "--ignore-not-found=true" , "--force" }
189
+ if obj .namespace != "" {
190
+ args = append (args , "--namespace" , obj .namespace )
191
+ }
192
+ output , err := exec .Command (c .client , args ... ).CombinedOutput ()
193
+ require .NoError (t , err , "Error deleting %q %q in namespace %q: %v" , obj .resourceName , obj .name , obj .namespace , string (output ))
194
+ }
168
195
169
196
// Create a context with a 60-second timeout.
170
197
ctx , cancel := context .WithTimeout (context .Background (), 60 * time .Second )
171
198
defer cancel ()
172
199
173
- // Wait for the ClusterRoleBinding to be deleted.
174
- if err := waitForDeletion (ctx , c .client , "clusterrolebinding" , c .clusterBinding ); err != nil {
175
- c .t .Logf ("Error waiting for clusterrolebinding deletion: %v" , err )
176
- } else {
177
- c .t .Log ("ClusterRoleBinding deleted" )
178
- }
179
-
180
- // Wait for the Pod to be deleted.
181
- if err := waitForDeletion (ctx , c .client , "pod" , c .curlPodName , "-n" , c .namespace ); err != nil {
182
- c .t .Logf ("Error waiting for pod deletion: %v" , err )
183
- } else {
184
- c .t .Log ("Pod deleted" )
200
+ for _ , obj := range objects {
201
+ err := waitForDeletion (ctx , c .client , obj .resourceName , obj .name , obj .namespace )
202
+ require .NoError (t , err , "Error deleting %q %q in namespace %q" , obj .resourceName , obj .name , obj .namespace )
203
+ t .Logf ("Successfully deleted %q %q in namespace %q" , obj .resourceName , obj .name , obj .namespace )
185
204
}
186
205
}
187
206
188
207
// waitForDeletion uses "kubectl wait" to block until the specified resource is deleted
189
208
// or until the 60-second timeout is reached.
190
- func waitForDeletion (ctx context.Context , client , resourceType , resourceName string , extraArgs ... string ) error {
191
- args := []string {"wait" , "--for=delete" , resourceType , resourceName }
192
- args = append (args , extraArgs ... )
193
- args = append (args , "--timeout=60s" )
209
+ func waitForDeletion (ctx context.Context , client , resourceType , resourceName , resourceNamespace string ) error {
210
+ args := []string {"wait" , "--for=delete" , "--timeout=60s" , resourceType , resourceName }
211
+ if resourceNamespace != "" {
212
+ args = append (args , "--namespace" , resourceNamespace )
213
+ }
194
214
cmd := exec .CommandContext (ctx , client , args ... )
195
215
output , err := cmd .CombinedOutput ()
196
216
if err != nil {
@@ -199,6 +219,17 @@ func waitForDeletion(ctx context.Context, client, resourceType, resourceName str
199
219
return nil
200
220
}
201
221
222
+ // createRandomNamespace creates a random namespace
223
+ func createRandomNamespace (t * testing.T , client string ) string {
224
+ nsName := fmt .Sprintf ("testns-%s" , rand .String (8 ))
225
+
226
+ cmd := exec .Command (client , "create" , "namespace" , nsName )
227
+ output , err := cmd .CombinedOutput ()
228
+ require .NoError (t , err , "Error creating namespace: %s" , string (output ))
229
+
230
+ return nsName
231
+ }
232
+
202
233
// getComponentNamespace returns the namespace where operator-controller or catalogd is running
203
234
func getComponentNamespace (t * testing.T , client , selector string ) string {
204
235
cmd := exec .Command (client , "get" , "pods" , "--all-namespaces" , "--selector=" + selector , "--output=jsonpath={.items[0].metadata.namespace}" )
0 commit comments