@@ -19,6 +19,7 @@ import (
1919 "fmt"
2020 "io"
2121 "os/exec"
22+ "strings"
2223 "testing"
2324 "time"
2425
@@ -33,16 +34,17 @@ func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
3334 client := testutil .FindK8sClient (t )
3435 curlNamespace := createRandomNamespace (t , client )
3536 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 )
3737
3838 config := NewMetricsTestConfig (
3939 client ,
4040 curlNamespace ,
41+ componentNamespace ,
4142 "operator-controller-metrics-reader" ,
4243 "operator-controller-metrics-binding" ,
4344 "operator-controller-metrics-reader" ,
4445 "oper-curl-metrics" ,
45- metricsURL ,
46+ "app.kubernetes.io/name=operator-controller" ,
47+ operatorControllerMetricsPort ,
4648 )
4749
4850 config .run (t )
@@ -53,42 +55,47 @@ func TestCatalogdMetricsExportedEndpoint(t *testing.T) {
5355 client := testutil .FindK8sClient (t )
5456 curlNamespace := createRandomNamespace (t , client )
5557 componentNamespace := getComponentNamespace (t , client , "control-plane=catalogd-controller-manager" )
56- metricsURL := fmt .Sprintf ("https://catalogd-service.%s.svc.cluster.local:7443/metrics" , componentNamespace )
5758
5859 config := NewMetricsTestConfig (
5960 client ,
6061 curlNamespace ,
62+ componentNamespace ,
6163 "catalogd-metrics-reader" ,
6264 "catalogd-metrics-binding" ,
6365 "catalogd-metrics-reader" ,
6466 "catalogd-curl-metrics" ,
65- metricsURL ,
67+ "app.kubernetes.io/name=catalogd" ,
68+ catalogdMetricsPort ,
6669 )
6770
6871 config .run (t )
6972}
7073
7174// MetricsTestConfig holds the necessary configurations for testing metrics endpoints.
7275type MetricsTestConfig struct {
73- client string
74- namespace string
75- clusterRole string
76- clusterBinding string
77- serviceAccount string
78- curlPodName string
79- metricsURL string
76+ client string
77+ namespace string
78+ componentNamespace string
79+ clusterRole string
80+ clusterBinding string
81+ serviceAccount string
82+ curlPodName string
83+ componentSelector string
84+ metricsPort int
8085}
8186
8287// NewMetricsTestConfig initializes a new MetricsTestConfig.
83- func NewMetricsTestConfig (client , namespace , clusterRole , clusterBinding , serviceAccount , curlPodName , metricsURL string ) * MetricsTestConfig {
88+ func NewMetricsTestConfig (client , namespace , componentNamespace , clusterRole , clusterBinding , serviceAccount , curlPodName , componentSelector string , metricsPort int ) * MetricsTestConfig {
8489 return & MetricsTestConfig {
85- client : client ,
86- namespace : namespace ,
87- clusterRole : clusterRole ,
88- clusterBinding : clusterBinding ,
89- serviceAccount : serviceAccount ,
90- curlPodName : curlPodName ,
91- metricsURL : metricsURL ,
90+ client : client ,
91+ namespace : namespace ,
92+ componentNamespace : componentNamespace ,
93+ clusterRole : clusterRole ,
94+ clusterBinding : clusterBinding ,
95+ serviceAccount : serviceAccount ,
96+ curlPodName : curlPodName ,
97+ componentSelector : componentSelector ,
98+ metricsPort : metricsPort ,
9299 }
93100}
94101
@@ -154,19 +161,33 @@ func (c *MetricsTestConfig) createCurlMetricsPod(t *testing.T) {
154161 require .NoError (t , err , "Error creating curl pod: %s" , string (output ))
155162}
156163
157- // validate verifies if is possible to access the metrics
164+ // validate verifies if is possible to access the metrics from all pods
158165func (c * MetricsTestConfig ) validate (t * testing.T , token string ) {
159166 t .Log ("Waiting for the curl pod to be ready" )
160167 waitCmd := exec .Command (c .client , "wait" , "--for=condition=Ready" , "pod" , c .curlPodName , "--namespace" , c .namespace , "--timeout=60s" )
161168 waitOutput , waitErr := waitCmd .CombinedOutput ()
162169 require .NoError (t , waitErr , "Error waiting for curl pod to be ready: %s" , string (waitOutput ))
163170
164- t .Log ("Validating the metrics endpoint" )
165- curlCmd := exec .Command (c .client , "exec" , c .curlPodName , "--namespace" , c .namespace , "--" ,
166- "curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , c .metricsURL )
167- output , err := curlCmd .CombinedOutput ()
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" )
171+ // Get all pod IPs for the component
172+ podIPs := c .getComponentPodIPs (t )
173+ require .NotEmpty (t , podIPs , "No pod IPs found for component" )
174+ t .Logf ("Found %d pod(s) to scrape metrics from" , len (podIPs ))
175+
176+ // Validate metrics endpoint for each pod
177+ for i , podIP := range podIPs {
178+ // Build metrics URL with pod FQDN: <pod-ip-with-dashes>.<namespace>.pod.cluster.local
179+ // Convert IP dots to dashes (e.g., 10.244.0.11 -> 10-244-0-11)
180+ podIPDashes := strings .ReplaceAll (podIP , "." , "-" )
181+ metricsURL := fmt .Sprintf ("https://%s.%s.pod.cluster.local:%d/metrics" , podIPDashes , c .componentNamespace , c .metricsPort )
182+ t .Logf ("Validating metrics endpoint for pod %d/%d: %s" , i + 1 , len (podIPs ), metricsURL )
183+
184+ curlCmd := exec .Command (c .client , "exec" , c .curlPodName , "--namespace" , c .namespace , "--" ,
185+ "curl" , "-v" , "-k" , "-H" , "Authorization: Bearer " + token , metricsURL )
186+ output , err := curlCmd .CombinedOutput ()
187+ require .NoError (t , err , "Error calling metrics endpoint %s: %s" , metricsURL , string (output ))
188+ require .Contains (t , string (output ), "200 OK" , "Metrics endpoint %s did not return 200 OK" , metricsURL )
189+ t .Logf ("Successfully scraped metrics from pod %d/%d" , i + 1 , len (podIPs ))
190+ }
170191}
171192
172193// cleanup removes the created resources. Uses a context with timeout to prevent hangs.
@@ -243,6 +264,29 @@ func getComponentNamespace(t *testing.T, client, selector string) string {
243264 return namespace
244265}
245266
267+ // getComponentPodIPs returns the IP addresses of all pods matching the component selector
268+ func (c * MetricsTestConfig ) getComponentPodIPs (t * testing.T ) []string {
269+ cmd := exec .Command (c .client , "get" , "pods" ,
270+ "--namespace=" + c .componentNamespace ,
271+ "--selector=" + c .componentSelector ,
272+ "--output=jsonpath={.items[*].status.podIP}" )
273+ output , err := cmd .CombinedOutput ()
274+ require .NoError (t , err , "Error getting pod IPs: %s" , string (output ))
275+
276+ podIPsStr := string (bytes .TrimSpace (output ))
277+ if podIPsStr == "" {
278+ return []string {}
279+ }
280+
281+ // Split space-separated IPs
282+ fields := bytes .Fields ([]byte (podIPsStr ))
283+ ips := make ([]string , len (fields ))
284+ for i , field := range fields {
285+ ips [i ] = string (field )
286+ }
287+ return ips
288+ }
289+
246290func stdoutAndCombined (cmd * exec.Cmd ) ([]byte , []byte , error ) {
247291 var outOnly , outAndErr bytes.Buffer
248292 allWriter := io .MultiWriter (& outOnly , & outAndErr )
0 commit comments