@@ -17,6 +17,8 @@ import (
1717 "bytes"
1818 "context"
1919 "errors"
20+ "fmt"
21+ "os"
2022 "strings"
2123 "testing"
2224 "time"
@@ -36,8 +38,8 @@ import (
3638 "sigs.k8s.io/controller-runtime/pkg/client/config"
3739)
3840
39- // TestOperatorControllerMetricsExportedEndpoint verifies that the metrics endpoint for the operator controller
40- func TestOperatorControllerMetricsExportedEndpoint (t * testing.T ) {
41+ // FetchOperatorControllerMetricsExportedEndpoint verifies that the metrics endpoint for the operator controller
42+ func FetchOperatorControllerMetricsExportedEndpoint (t * testing.T ) {
4143 kubeClient , restConfig := findK8sClient (t )
4244 mtc := NewMetricsTestConfig (
4345 t ,
@@ -49,13 +51,14 @@ func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
4951 "operator-controller-controller-manager" ,
5052 "oper-curl-metrics" ,
5153 "https://operator-controller-service.NAMESPACE.svc.cluster.local:8443/metrics" ,
54+ "oper-con" ,
5255 )
5356
5457 mtc .run ()
5558}
5659
57- // TestCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for catalogd
58- func TestCatalogdMetricsExportedEndpoint (t * testing.T ) {
60+ // FetchCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for catalogd
61+ func FetchCatalogdMetricsExportedEndpoint (t * testing.T ) {
5962 kubeClient , restConfig := findK8sClient (t )
6063 mtc := NewMetricsTestConfig (
6164 t ,
@@ -67,11 +70,68 @@ func TestCatalogdMetricsExportedEndpoint(t *testing.T) {
6770 "catalogd-controller-manager" ,
6871 "catalogd-curl-metrics" ,
6972 "https://catalogd-service.NAMESPACE.svc.cluster.local:7443/metrics" ,
73+ "catalogd" ,
7074 )
7175
7276 mtc .run ()
7377}
7478
79+ // fetchMetrics retrieves Prometheus metrics from the endpoint
80+ func (c * MetricsTestConfig ) fetchMetrics (ctx context.Context , token string ) string {
81+ c .t .Log ("Fetching Prometheus metrics after test execution" )
82+
83+ // Command to execute inside the pod
84+ cmd := []string {
85+ "curl" , "-s" , "-k" ,
86+ "-H" , "Authorization: Bearer " + token ,
87+ c .metricsURL ,
88+ }
89+
90+ // Execute command in pod
91+ req := c .kubeClient .CoreV1 ().RESTClient ().
92+ Post ().
93+ Resource ("pods" ).
94+ Namespace (c .namespace ).
95+ Name (c .curlPodName ).
96+ SubResource ("exec" ).
97+ VersionedParams (& corev1.PodExecOptions {
98+ Container : "curl" ,
99+ Command : cmd ,
100+ Stdin : false ,
101+ Stdout : true ,
102+ Stderr : true ,
103+ TTY : false ,
104+ }, scheme .ParameterCodec )
105+
106+ executor , err := remotecommand .NewSPDYExecutor (c .restConfig , "POST" , req .URL ())
107+ require .NoError (c .t , err , "Error creating SPDY executor" )
108+
109+ var stdout , stderr bytes.Buffer
110+ streamOpts := remotecommand.StreamOptions {
111+ Stdout : & stdout ,
112+ Stderr : & stderr ,
113+ }
114+
115+ err = executor .StreamWithContext (ctx , streamOpts )
116+ require .NoError (c .t , err , "Error streaming exec request: %v" , stderr .String ())
117+
118+ return stdout .String ()
119+ }
120+
121+ // saveMetricsToFile writes the fetched metrics to a text file
122+ func (c * MetricsTestConfig ) saveMetricsToFile (metrics string ) {
123+ dir := "results"
124+ if err := os .MkdirAll (dir , os .ModePerm ); err != nil {
125+ c .t .Fatalf ("Failed to create directory %s: %v" , dir , err )
126+ }
127+
128+ filePath := fmt .Sprintf ("%s/metrics_%s_%s.txt" , dir , c .name , c .t .Name ())
129+ err := os .WriteFile (filePath , []byte (metrics ), 0644 )
130+ require .NoError (c .t , err , "Failed to save metrics to file" )
131+
132+ c .t .Logf ("Metrics saved to: %s" , filePath )
133+ }
134+
75135func findK8sClient (t * testing.T ) (kubernetes.Interface , * rest.Config ) {
76136 cfg , err := config .GetConfig ()
77137 require .NoError (t , err , "Failed to get Kubernetes config" )
@@ -94,6 +154,7 @@ type MetricsTestConfig struct {
94154 serviceAccount string
95155 curlPodName string
96156 metricsURL string
157+ name string
97158}
98159
99160// NewMetricsTestConfig initializes a new MetricsTestConfig.
@@ -107,6 +168,7 @@ func NewMetricsTestConfig(
107168 serviceAccount string ,
108169 curlPodName string ,
109170 metricsURL string ,
171+ name string ,
110172) * MetricsTestConfig {
111173 // Discover which namespace the relevant Pod is running in
112174 namespace := getComponentNamespace (t , kubeClient , selector )
@@ -124,24 +186,41 @@ func NewMetricsTestConfig(
124186 serviceAccount : serviceAccount ,
125187 curlPodName : curlPodName ,
126188 metricsURL : metricsURL ,
189+ name : name ,
127190 }
128191}
129192
130193// run executes the entire test flow
131194func (c * MetricsTestConfig ) run () {
132195 ctx := context .Background ()
133- defer c .cleanup (ctx )
196+ // To speed up
197+ // defer c.cleanup(ctx)
134198 c .createMetricsClusterRoleBinding (ctx )
135199 token := c .getServiceAccountToken (ctx )
136200 c .createCurlMetricsPod (ctx )
137201 c .waitForPodReady (ctx )
138202 // Exec `curl` in the Pod to validate the metrics
139203 c .validateMetricsEndpoint (ctx , token )
204+
205+ // Fetch and save Prometheus metrics after test execution
206+ metrics := c .fetchMetrics (ctx , token )
207+ c .saveMetricsToFile (metrics )
140208}
141209
142210// createMetricsClusterRoleBinding to bind the cluster role so metrics are accessible
143211func (c * MetricsTestConfig ) createMetricsClusterRoleBinding (ctx context.Context ) {
144- c .t .Logf ("Creating ClusterRoleBinding %q in namespace %q" , c .clusterBinding , c .namespace )
212+ c .t .Logf ("Ensuring ClusterRoleBinding %q exists in namespace %q" , c .clusterBinding , c .namespace )
213+
214+ _ , err := c .kubeClient .RbacV1 ().ClusterRoleBindings ().Get (ctx , c .clusterBinding , metav1.GetOptions {})
215+ if err == nil {
216+ c .t .Logf ("ClusterRoleBinding %q already exists, skipping creation" , c .clusterBinding )
217+ return
218+ }
219+
220+ if ! apierrors .IsNotFound (err ) {
221+ require .NoError (c .t , err , "Error checking for existing ClusterRoleBinding" )
222+ return
223+ }
145224
146225 crb := & rbacv1.ClusterRoleBinding {
147226 ObjectMeta : metav1.ObjectMeta {
@@ -161,8 +240,9 @@ func (c *MetricsTestConfig) createMetricsClusterRoleBinding(ctx context.Context)
161240 },
162241 }
163242
164- _ , err : = c .kubeClient .RbacV1 ().ClusterRoleBindings ().Create (ctx , crb , metav1.CreateOptions {})
243+ _ , err = c .kubeClient .RbacV1 ().ClusterRoleBindings ().Create (ctx , crb , metav1.CreateOptions {})
165244 require .NoError (c .t , err , "Error creating ClusterRoleBinding" )
245+ c .t .Logf ("Successfully created ClusterRoleBinding %q" , c .clusterBinding )
166246}
167247
168248// getServiceAccountToken creates a TokenRequest for the service account
@@ -188,7 +268,18 @@ func (c *MetricsTestConfig) getServiceAccountToken(ctx context.Context) string {
188268
189269// createCurlMetricsPod spawns a pod running `curlimages/curl` to check metrics
190270func (c * MetricsTestConfig ) createCurlMetricsPod (ctx context.Context ) {
191- c .t .Logf ("Creating curl pod (%s/%s) to validate the metrics endpoint" , c .namespace , c .curlPodName )
271+ c .t .Logf ("Ensuring curl pod (%s/%s) exists to validate the metrics endpoint" , c .namespace , c .curlPodName )
272+
273+ _ , err := c .kubeClient .CoreV1 ().Pods (c .namespace ).Get (ctx , c .curlPodName , metav1.GetOptions {})
274+ if err == nil {
275+ c .t .Logf ("Curl pod %q already exists, skipping creation" , c .curlPodName )
276+ return
277+ }
278+
279+ if ! apierrors .IsNotFound (err ) {
280+ require .NoError (c .t , err , "Error checking for existing curl pod" )
281+ return
282+ }
192283
193284 pod := & corev1.Pod {
194285 ObjectMeta : metav1.ObjectMeta {
@@ -220,7 +311,7 @@ func (c *MetricsTestConfig) createCurlMetricsPod(ctx context.Context) {
220311 },
221312 }
222313
223- _ , err : = c .kubeClient .CoreV1 ().Pods (c .namespace ).Create (ctx , pod , metav1.CreateOptions {})
314+ _ , err = c .kubeClient .CoreV1 ().Pods (c .namespace ).Create (ctx , pod , metav1.CreateOptions {})
224315 require .NoError (c .t , err , "Error creating curl pod" )
225316}
226317
0 commit comments