@@ -11,7 +11,6 @@ import (
1111 "os/exec"
1212 "strings"
1313 "testing"
14- "time"
1514
1615 prommodel "github.com/prometheus/client_model/go"
1716 "github.com/prometheus/common/expfmt"
@@ -486,7 +485,7 @@ func getTemplateData() (templateData, []Template) {
486485}
487486
488487func fetchAndParsePrometheusMetrics (t * testing.T , cmd string ) map [string ]* prommodel.MetricFamily {
489- out , _ , err := ExecCommandOnSpecificPod (t , clientName , testNamespace , cmd )
488+ out , _ , err := ExecCommandOnSpecificPodWithoutTTY (t , clientName , testNamespace , cmd )
490489 assert .NoErrorf (t , err , "cannot execute command - %s" , err )
491490
492491 parser := expfmt.TextParser {}
@@ -498,6 +497,33 @@ func fetchAndParsePrometheusMetrics(t *testing.T, cmd string) map[string]*prommo
498497 return families
499498}
500499
500+ // WaitForPrometheusMetric waits for a specific metric to appear in the Prometheus metrics endpoint
501+ // and validates that the MetricFamily it has certain conditions using the provided familyValidator function.
502+ // Returns the parsed MetricFamily.
503+ func WaitForPrometheusMetric (t * testing.T , metricToWaitFor string , familyValidator func (family * prommodel.MetricFamily ) bool ) map [string ]* prommodel.MetricFamily {
504+ contextWithTimeout , cancel := context .WithTimeout (context .Background (), WaitShort )
505+ defer cancel ()
506+ var family map [string ]* prommodel.MetricFamily
507+ err := KedaEventually (contextWithTimeout , func (ctx context.Context ) (bool , error ) {
508+ t .Logf ("Waiting for metric %s" , metricToWaitFor )
509+ family = fetchAndParsePrometheusMetrics (t , fmt .Sprintf ("curl --insecure %s" , kedaOperatorPrometheusURL ))
510+
511+ if _ , ok := family [metricToWaitFor ]; ok {
512+ if familyValidator (family [metricToWaitFor ]) {
513+ return true , nil
514+ }
515+ return false , nil
516+ }
517+ return false , nil
518+ }, IntervalShort )
519+
520+ if err != nil {
521+ t .Errorf ("error waiting for metric %s: %v" , metricToWaitFor , err )
522+ }
523+
524+ return family
525+ }
526+
501527func testScalerMetricValue (t * testing.T ) {
502528 t .Log ("--- testing scaler metric value ---" )
503529
@@ -528,110 +554,78 @@ func testScaledObjectErrors(t *testing.T, data templateData) {
528554 t .Log ("--- testing scaled object errors ---" )
529555
530556 KubectlDeleteWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
531- time .Sleep (2 * time .Second )
532557 KubectlApplyWithTemplate (t , data , "wrongScaledObjectTemplate" , wrongScaledObjectTemplate )
533558
534- // wait for 2 seconds as pollinginterval is 2
535- time . Sleep ( 20 * time . Second )
559+ WaitForPrometheusMetric ( t , "keda_scaled_object_errors_total" , func ( family * prommodel. MetricFamily ) bool {
560+ errCounterVal1 := getErrorMetricsValue ( family )
536561
537- family := fetchAndParsePrometheusMetrics ( t , fmt . Sprintf ( "curl --insecure %s" , kedaOperatorPrometheusURL ))
538- val , ok := family [ "keda_scaled_object_errors_total" ]
539- assert . True ( t , ok , "keda_scaled_object_errors_total not available" )
540- if ok {
541- errCounterVal1 := getErrorMetricsValue ( val )
562+ // in the nested wait, we are just waiting for errCounterVal2 to eventually be greater that the first errCounterVal1
563+ metrics2 := WaitForPrometheusMetric ( t , "keda_scaled_object_errors_total" , func ( family * prommodel. MetricFamily ) bool {
564+ errCounterVal2 := getErrorMetricsValue ( family )
565+ return errCounterVal2 > errCounterVal1 && errCounterVal2 > 0
566+ } )
542567
543- // wait for 2 seconds as pollinginterval is 2
544- time .Sleep (2 * time .Second )
568+ errCounterVal2 := getErrorMetricsValue (metrics2 ["keda_scaled_object_errors_total" ])
545569
546- family = fetchAndParsePrometheusMetrics (t , fmt .Sprintf ("curl --insecure %s" , kedaOperatorPrometheusURL ))
547- val , ok := family ["keda_scaled_object_errors_total" ]
548- assert .True (t , ok , "keda_scaled_object_errors_total not available" )
549- if ok {
550- errCounterVal2 := getErrorMetricsValue (val )
551- assert .NotEqual (t , errCounterVal2 , float64 (0 ))
552- assert .GreaterOrEqual (t , errCounterVal2 , errCounterVal1 )
553- }
554- }
570+ // we don't have to check again, but extra validation is fine
571+ return errCounterVal2 > errCounterVal1 && errCounterVal2 > 0
572+ })
555573
556574 KubectlDeleteWithTemplate (t , data , "wrongScaledObjectTemplate" , wrongScaledObjectTemplate )
557- time .Sleep (2 * time .Second )
558575 KubectlApplyWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
559576}
560577
561578func testScaledJobErrors (t * testing.T , data templateData ) {
562579 t .Log ("--- testing scaled job errors ---" )
563580
564581 KubectlDeleteWithTemplate (t , data , "scaledJobTemplate" , scaledJobTemplate )
565- time .Sleep (2 * time .Second )
566582 KubectlApplyWithTemplate (t , data , "wrongScaledJobTemplate" , wrongScaledJobTemplate )
567583
568- // wait for 2 seconds as pollinginterval is 2
569- time . Sleep ( 20 * time . Second )
584+ WaitForPrometheusMetric ( t , "keda_scaled_job_errors_total" , func ( family * prommodel. MetricFamily ) bool {
585+ errCounterVal1 := getErrorMetricsValue ( family )
570586
571- family := fetchAndParsePrometheusMetrics ( t , fmt . Sprintf ( "curl --insecure %s" , kedaOperatorPrometheusURL ))
572- val , ok := family [ "keda_scaled_job_errors_total" ]
573- assert . True ( t , ok , "keda_scaled_job_errors_total not available" )
574- if ok {
575- errCounterVal1 := getErrorMetricsValue ( val )
587+ // in the nested wait, we are just waiting for errCounterVal2 to eventually be greater that the first errCounterVal1
588+ metrics2 := WaitForPrometheusMetric ( t , "keda_scaled_job_errors_total" , func ( family * prommodel. MetricFamily ) bool {
589+ errCounterVal2 := getErrorMetricsValue ( family )
590+ return errCounterVal2 > errCounterVal1 && errCounterVal2 > 0
591+ } )
576592
577- // wait for 2 seconds as pollinginterval is 2
578- time .Sleep (2 * time .Second )
593+ errCounterVal2 := getErrorMetricsValue (metrics2 ["keda_scaled_job_errors_total" ])
579594
580- family = fetchAndParsePrometheusMetrics (t , fmt .Sprintf ("curl --insecure %s" , kedaOperatorPrometheusURL ))
581- val , ok := family ["keda_scaled_job_errors_total" ]
582- assert .True (t , ok , "keda_scaled_job_errors_total not available" )
583- if ok {
584- errCounterVal2 := getErrorMetricsValue (val )
585- assert .NotEqual (t , errCounterVal2 , float64 (0 ))
586- assert .GreaterOrEqual (t , errCounterVal2 , errCounterVal1 )
587- } else {
588- t .Errorf ("metric keda_scaled_job_errors_total not available" )
589- }
590- } else {
591- t .Errorf ("metric keda_scaled_job_errors_total not available" )
592- }
595+ // we don't have to check again, but extra validation is fine
596+ return errCounterVal2 > errCounterVal1 && errCounterVal2 > 0
597+ })
593598
594599 KubectlDeleteWithTemplate (t , data , "wrongScaledJobTemplate" , wrongScaledJobTemplate )
595- time .Sleep (2 * time .Second )
596600 KubectlApplyWithTemplate (t , data , "scaledJobTemplate" , scaledJobTemplate )
597601}
598602
599603func testScalerErrors (t * testing.T , data templateData ) {
600604 t .Log ("--- testing scaler errors ---" )
601605
602606 KubectlDeleteWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
603- time .Sleep (2 * time .Second )
604607 KubectlApplyWithTemplate (t , data , "wrongScaledObjectTemplate" , wrongScaledObjectTemplate )
605-
606608 KubectlDeleteWithTemplate (t , data , "scaledJobTemplate" , scaledJobTemplate )
607- time .Sleep (2 * time .Second )
608609 KubectlApplyWithTemplate (t , data , "wrongScaledJobTemplate" , wrongScaledJobTemplate )
609610
610- family := fetchAndParsePrometheusMetrics (t , fmt .Sprintf ("curl --insecure %s" , kedaOperatorPrometheusURL ))
611- val , ok := family ["keda_scaler_detail_errors_total" ]
612- assert .True (t , ok , "keda_scaler_detail_errors_total not available" )
613- if ok {
614- errCounterVal1 := getErrorMetricsValue (val )
611+ WaitForPrometheusMetric (t , "keda_scaler_detail_errors_total" , func (family * prommodel.MetricFamily ) bool {
612+ errCounterVal1 := getErrorMetricsValue (family )
615613
616- // wait for 20 seconds to correctly fetch metrics.
617- time .Sleep (20 * time .Second )
614+ // in the nested wait, we are just waiting for errCounterVal2 to eventually be greater that the first errCounterVal1
615+ metrics2 := WaitForPrometheusMetric (t , "keda_scaler_detail_errors_total" , func (family * prommodel.MetricFamily ) bool {
616+ errCounterVal2 := getErrorMetricsValue (family )
617+ return errCounterVal2 > errCounterVal1 && errCounterVal2 > 0
618+ })
618619
619- family = fetchAndParsePrometheusMetrics (t , fmt .Sprintf ("curl --insecure %s" , kedaOperatorPrometheusURL ))
620- val , ok := family ["keda_scaler_detail_errors_total" ]
621- assert .True (t , ok , "keda_scaler_detail_errors_total not available" )
622- if ok {
623- errCounterVal2 := getErrorMetricsValue (val )
624- assert .NotEqual (t , errCounterVal2 , float64 (0 ))
625- assert .GreaterOrEqual (t , errCounterVal2 , errCounterVal1 )
626- }
627- }
620+ errCounterVal2 := getErrorMetricsValue (metrics2 ["keda_scaler_detail_errors_total" ])
621+
622+ // we don't have to check again, but extra validation is fine
623+ return errCounterVal2 > errCounterVal1 && errCounterVal2 > 0
624+ })
628625
629626 KubectlDeleteWithTemplate (t , data , "wrongScaledJobTemplate" , wrongScaledJobTemplate )
630- time .Sleep (2 * time .Second )
631627 KubectlApplyWithTemplate (t , data , "scaledJobTemplate" , scaledJobTemplate )
632-
633628 KubectlDeleteWithTemplate (t , data , "wrongScaledObjectTemplate" , wrongScaledObjectTemplate )
634- time .Sleep (2 * time .Second )
635629 KubectlApplyWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
636630}
637631
@@ -827,18 +821,41 @@ func testScaledObjectPausedMetric(t *testing.T, data templateData) {
827821
828822 // Pause the ScaledObject
829823 KubectlApplyWithTemplate (t , data , "scaledObjectPausedTemplate" , scaledObjectPausedTemplate )
830- time .Sleep (20 * time .Second )
831824
832825 // Check that the paused metric is now true
833- families := fetchAndParsePrometheusMetrics (t , fmt .Sprintf ("curl --insecure %s" , kedaOperatorPrometheusURL ))
826+ families := WaitForPrometheusMetric (t , "keda_scaled_object_paused" , func (family * prommodel.MetricFamily ) bool {
827+ metricValue := 0.0
828+ metrics := family .GetMetric ()
829+ for _ , metric := range metrics {
830+ labels := metric .GetLabel ()
831+ for _ , label := range labels {
832+ if * label .Name == labelScaledObject && * label .Value == scaledObjectName {
833+ metricValue = * metric .Gauge .Value
834+ }
835+ }
836+ }
837+
838+ return metricValue == float64 (1 )
839+ })
834840 assertScaledObjectPausedMetric (t , families , scaledObjectName , true )
835841
836842 // Unpause the ScaledObject
837843 KubectlApplyWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
838- time .Sleep (20 * time .Second )
839844
840845 // Check that the paused metric is back to false
841- families = fetchAndParsePrometheusMetrics (t , fmt .Sprintf ("curl --insecure %s" , kedaOperatorPrometheusURL ))
846+ families = WaitForPrometheusMetric (t , "keda_scaled_object_paused" , func (family * prommodel.MetricFamily ) bool {
847+ metricValue := 0.0
848+ metrics := family .GetMetric ()
849+ for _ , metric := range metrics {
850+ labels := metric .GetLabel ()
851+ for _ , label := range labels {
852+ if * label .Name == labelScaledObject && * label .Value == scaledObjectName {
853+ metricValue = * metric .Gauge .Value
854+ }
855+ }
856+ }
857+ return metricValue == float64 (0 )
858+ })
842859 assertScaledObjectPausedMetric (t , families , scaledObjectName , false )
843860}
844861
@@ -1350,62 +1367,64 @@ func testCloudEventEmitted(t *testing.T, data templateData) {
13501367 t .Log ("--- testing cloudevent emitted ---" )
13511368
13521369 KubectlDeleteWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
1370+
13531371 KubectlApplyWithTemplate (t , data , "cloudEventSourceTemplate" , cloudEventSourceTemplate )
13541372 KubectlApplyWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
13551373
1356- family := fetchAndParsePrometheusMetrics (t , fmt . Sprintf ( "curl --insecure %s" , kedaOperatorPrometheusURL ) )
1374+ defer KubectlDeleteWithTemplate (t , data , "cloudEventSourceTemplate" , cloudEventSourceTemplate )
13571375
1358- if val , ok := family [ "keda_cloudeventsource_events_emitted_total" ]; ok {
1359- var found bool
1360- metrics := val . GetMetric ()
1361- for _ , metric := range metrics {
1376+ familyValidator := func ( family * prommodel. MetricFamily ) bool {
1377+ labels := family . GetMetric ()
1378+ found := false
1379+ for _ , metric := range labels {
13621380 labels := metric .GetLabel ()
13631381 if len (labels ) >= 4 &&
13641382 ExtractPrometheusLabelValue ("cloudeventsource" , labels ) == "prometheus-metrics-test-ce" &&
13651383 ExtractPrometheusLabelValue ("eventsink" , labels ) == "http" &&
13661384 ExtractPrometheusLabelValue ("namespace" , labels ) == "prometheus-metrics-test-ns" &&
1367- ExtractPrometheusLabelValue ("state" , labels ) == "emitted" {
1368- assert . GreaterOrEqual ( t , * metric . Counter . Value , float64 ( 1 ))
1385+ ExtractPrometheusLabelValue ("state" , labels ) == "emitted" &&
1386+ metric . GetCounter (). GetValue () >= 1 {
13691387 found = true
13701388 }
13711389 }
1372- assert .Equal (t , true , found )
1373- } else {
1374- t .Errorf ("metric not available" )
1390+ return found
13751391 }
1392+
1393+ families := WaitForPrometheusMetric (t , "keda_cloudeventsource_events_emitted_total" , familyValidator )
1394+ metric := families ["keda_cloudeventsource_events_emitted_total" ]
1395+
1396+ assert .True (t , familyValidator (metric ))
13761397}
13771398
13781399func testCloudEventEmittedError (t * testing.T , data templateData ) {
13791400 t .Log ("--- testing cloudevent emitted error ---" )
13801401
13811402 KubectlDeleteWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
1382- KubectlDeleteWithTemplate ( t , data , "cloudEventSourceTemplate" , cloudEventSourceTemplate )
1403+
13831404 KubectlApplyWithTemplate (t , data , "wrongCloudEventSourceTemplate" , wrongCloudEventSourceTemplate )
1384- time .Sleep (1 * time .Second )
13851405 KubectlApplyWithTemplate (t , data , "scaledObjectTemplate" , scaledObjectTemplate )
1386- time .Sleep (5 * time .Second )
13871406
1388- family := fetchAndParsePrometheusMetrics (t , fmt . Sprintf ( "curl --insecure %s" , kedaOperatorPrometheusURL ) )
1407+ defer KubectlDeleteWithTemplate (t , data , "wrongCloudEventSourceTemplate" , wrongCloudEventSourceTemplate )
13891408
1390- if val , ok := family [ "keda_cloudeventsource_events_emitted_total" ]; ok {
1391- var found bool
1392- metrics := val . GetMetric ()
1393- for _ , metric := range metrics {
1409+ familyValidator := func ( family * prommodel. MetricFamily ) bool {
1410+ labels := family . GetMetric ()
1411+ found := false
1412+ for _ , metric := range labels {
13941413 labels := metric .GetLabel ()
13951414 if len (labels ) >= 4 &&
13961415 ExtractPrometheusLabelValue ("cloudeventsource" , labels ) == "prometheus-metrics-test-ce-w" &&
13971416 ExtractPrometheusLabelValue ("eventsink" , labels ) == "http" &&
13981417 ExtractPrometheusLabelValue ("namespace" , labels ) == "prometheus-metrics-test-ns" &&
1399- ExtractPrometheusLabelValue ("state" , labels ) == "failed" {
1400- assert . GreaterOrEqual ( t , * metric . Counter . Value , float64 ( 5 ))
1418+ ExtractPrometheusLabelValue ("state" , labels ) == "failed" &&
1419+ metric . GetCounter (). GetValue () >= 5 {
14011420 found = true
14021421 }
14031422 }
1404- assert .Equal (t , true , found )
1405- } else {
1406- t .Errorf ("metric not available" )
1423+ return found
14071424 }
14081425
1409- KubectlDeleteWithTemplate (t , data , "wrongCloudEventSourceTemplate" , wrongCloudEventSourceTemplate )
1410- KubectlApplyWithTemplate (t , data , "cloudEventSourceTemplate" , cloudEventSourceTemplate )
1426+ families := WaitForPrometheusMetric (t , "keda_cloudeventsource_events_emitted_total" , familyValidator )
1427+ metric := families ["keda_cloudeventsource_events_emitted_total" ]
1428+
1429+ assert .True (t , familyValidator (metric ))
14111430}
0 commit comments