Skip to content

Commit c2d222d

Browse files
authored
Merge pull request #1281 from akrejcir/improve-flaky-metrics-tests
tests: Improve stability of metric tests
2 parents 41dbd15 + 26f715d commit c2d222d

7 files changed

+103
-38
lines changed

tests/commonTemplates_test.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ var _ = Describe("Common templates", func() {
189189
Entry("[test_id:5087]test template", &testTemplate),
190190
)
191191

192-
It("[test_id: 7340] should increase metrics when restoring tamplate", func() {
192+
It("[test_id: 7340] should increase metrics when restoring template", func() {
193193
expectTemplateUpdateToIncreaseTotalRestoredTemplatesCount(testTemplate)
194194
})
195195

@@ -355,7 +355,12 @@ var _ = Describe("Common templates", func() {
355355
})
356356

357357
func expectTemplateUpdateToIncreaseTotalRestoredTemplatesCount(testTemplate testResource) {
358-
restoredCountBefore := totalRestoredTemplatesCount()
358+
restoredCountBefore, err := totalRestoredTemplatesCount()
359+
Expect(err).ToNot(HaveOccurred())
360+
359361
expectRestoreAfterUpdate(&testTemplate)
360-
Expect(totalRestoredTemplatesCount() - restoredCountBefore).To(Equal(1))
362+
363+
restoredCountAfter, err := totalRestoredTemplatesCount()
364+
Expect(err).ToNot(HaveOccurred())
365+
Expect(restoredCountAfter - restoredCountBefore).To(Equal(1))
361366
}

tests/metrics_test.go

+15-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
common_templates "kubevirt.io/ssp-operator/internal/operands/common-templates"
2121
"kubevirt.io/ssp-operator/internal/operands/metrics"
2222
"kubevirt.io/ssp-operator/pkg/monitoring/rules"
23+
"kubevirt.io/ssp-operator/tests/env"
2324
)
2425

2526
func mergeMaps(maps ...map[string]string) map[string]string {
@@ -174,25 +175,35 @@ var _ = Describe("Metrics", func() {
174175
It("[test_id:TODO]should increment kubevirt_ssp_common_templates_restored_total during normal reconcile", func() {
175176
skipIfUpgradeLane()
176177

177-
restoredCount := totalRestoredTemplatesCount()
178+
var restoredCount int
179+
Eventually(func() error {
180+
var err error
181+
restoredCount, err = totalRestoredTemplatesCount()
182+
return err
183+
}, env.ShortTimeout(), time.Second).Should(Succeed())
178184

179185
template.Labels[common_templates.TemplateTypeLabel] = "test"
180186
Expect(apiClient.Update(ctx, template)).To(Succeed())
181187

182-
Eventually(func() int {
188+
Eventually(func() (int, error) {
183189
return totalRestoredTemplatesCount()
184190
}, 5*time.Minute, 10*time.Second).Should(Equal(restoredCount + 1))
185191
})
186192

187193
It("[test_id:TODO]should not increment kubevirt_ssp_common_templates_restored_total during upgrades", func() {
188-
restoredCount := totalRestoredTemplatesCount()
194+
var restoredCount int
195+
Eventually(func() error {
196+
var err error
197+
restoredCount, err = totalRestoredTemplatesCount()
198+
return err
199+
}, env.ShortTimeout(), time.Second).Should(Succeed())
189200

190201
template.Labels[common_templates.TemplateTypeLabel] = "test"
191202
template.Labels[common_templates.TemplateVersionLabel] = "v" + rand.String(5)
192203
Expect(apiClient.Update(ctx, template)).To(Succeed())
193204

194205
// TODO: replace 'Consistently' with a direct wait for the template update
195-
Consistently(func() int {
206+
Consistently(func() (int, error) {
196207
return totalRestoredTemplatesCount()
197208
}, 2*time.Minute, 20*time.Second).Should(Equal(restoredCount))
198209
})

tests/metrics_test_utils.go

+23-9
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package tests
33
import (
44
"context"
55
"crypto/tls"
6+
"errors"
67
"fmt"
78
"io"
89
"net"
910
"net/http"
1011
"regexp"
1112
"strconv"
1213

13-
. "github.com/onsi/gomega"
1414
v1 "k8s.io/api/core/v1"
1515
)
1616

@@ -20,10 +20,13 @@ var regexpForMetrics = map[string]*regexp.Regexp{
2020
"kubevirt_ssp_operator_reconcile_succeeded": regexp.MustCompile(`kubevirt_ssp_operator_reconcile_succeeded ([0-9]+)`),
2121
}
2222

23-
func intMetricValue(metricName string, metricsPort uint16, pod *v1.Pod) int {
23+
func intMetricValue(metricName string, metricsPort uint16, pod *v1.Pod) (value int, err error) {
2424
conn, err := portForwarder.Connect(pod, metricsPort)
25-
Expect(err).ToNot(HaveOccurred())
26-
defer conn.Close()
25+
if err != nil {
26+
return 0, fmt.Errorf("failed to connect port-forwarding: %w", err)
27+
}
28+
defer func() { err = errors.Join(err, conn.Close()) }()
29+
2730
client := &http.Client{
2831
Transport: &http.Transport{
2932
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
@@ -33,15 +36,26 @@ func intMetricValue(metricName string, metricsPort uint16, pod *v1.Pod) int {
3336
},
3437
}
3538
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/metrics", metricsPort))
36-
Expect(err).ToNot(HaveOccurred(), "Can't get metrics from %s", pod.Name)
37-
defer resp.Body.Close()
38-
body, _ := io.ReadAll(resp.Body)
39+
if err != nil {
40+
return 0, fmt.Errorf("failed to get metrics from %s: %w", pod.Name, err)
41+
}
42+
defer func() { err = errors.Join(err, resp.Body.Close()) }()
43+
44+
body, err := io.ReadAll(resp.Body)
45+
if err != nil {
46+
return 0, fmt.Errorf("failed to read metrics response body: %w", err)
47+
}
48+
3949
regex, ok := regexpForMetrics[metricName]
4050
if !ok {
4151
panic(fmt.Sprintf("metricName %s does not have a defined regexp string, please add one to the regexpForMetrics map", metricName))
4252
}
53+
4354
valueOfMetric := regex.FindSubmatch(body)
4455
intValue, err := strconv.Atoi(string(valueOfMetric[1]))
45-
Expect(err).ToNot(HaveOccurred())
46-
return intValue
56+
if err != nil {
57+
return 0, fmt.Errorf("failed to convert metric %s value to int: %w", metricName, err)
58+
}
59+
60+
return intValue, nil
4761
}

tests/misc_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,9 @@ func validateSspIsFailingToReconcileMetric() {
167167
}
168168
})
169169
// the reconcile cycle should now be failing, so the kubevirt_ssp_operator_reconcile_succeeded metric should be 0
170-
Eventually(func() int {
170+
Eventually(func() (int, error) {
171171
return sspOperatorReconcileSucceededCount()
172-
}, env.ShortTimeout(), time.Second).Should(Equal(0))
172+
}, env.ShortTimeout(), time.Second).Should(BeZero())
173173
}
174174

175175
var _ = Describe("SCC annotation", func() {

tests/port-forwarding.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func (p *portForwardConn) Close() error {
4040
func (p *portForwarderImpl) Connect(pod *core.Pod, remotePort uint16) (net.Conn, error) {
4141
streamConnection, err := p.createStreamConnection(pod)
4242
if err != nil {
43-
return nil, err
43+
return nil, fmt.Errorf("failed to create stream connection: %w", err)
4444
}
4545

4646
requestId := atomic.AddInt32(&p.requestId, 1)
@@ -53,16 +53,18 @@ func (p *portForwarderImpl) Connect(pod *core.Pod, remotePort uint16) (net.Conn,
5353
errorStream, err := streamConnection.CreateStream(headers)
5454
if err != nil {
5555
streamConnection.Close()
56-
return nil, err
56+
return nil, fmt.Errorf("failed to create error stream: %w", err)
5757
}
5858
// We will not write to error stream
59-
errorStream.Close()
59+
if err := errorStream.Close(); err != nil {
60+
return nil, fmt.Errorf("failed to close error stream: %w", err)
61+
}
6062

6163
headers.Set(core.StreamType, core.StreamTypeData)
6264
dataStream, err := streamConnection.CreateStream(headers)
6365
if err != nil {
6466
streamConnection.Close()
65-
return nil, err
67+
return nil, fmt.Errorf("failed to create data stream: %w", err)
6668
}
6769

6870
return &portForwardConn{
@@ -74,7 +76,7 @@ func (p *portForwarderImpl) Connect(pod *core.Pod, remotePort uint16) (net.Conn,
7476
func (p *portForwarderImpl) createStreamConnection(pod *core.Pod) (httpstream.Connection, error) {
7577
transport, upgrader, err := spdy.RoundTripperFor(p.config)
7678
if err != nil {
77-
return nil, err
79+
return nil, fmt.Errorf("failed to create RoundTripper: %w", err)
7880
}
7981

8082
req := p.client.Post().
@@ -85,7 +87,11 @@ func (p *portForwarderImpl) createStreamConnection(pod *core.Pod) (httpstream.Co
8587

8688
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", req.URL())
8789
streamConn, _, err := dialer.Dial(portforward.PortForwardProtocolV1Name)
88-
return streamConn, err
90+
if err != nil {
91+
return nil, fmt.Errorf("failed to dial: %w", err)
92+
}
93+
94+
return streamConn, nil
8995
}
9096

9197
func NewPortForwarder(config *rest.Config, client rest.Interface) PortForwarder {

tests/tests_common_test.go

+16-9
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,27 @@ func expectRecreateAfterDelete(res *testResource) {
114114
Expect(err).ToNot(HaveOccurred())
115115
}
116116

117-
func sspOperatorReconcileSucceededCount() (sum int) {
118-
operatorPods, operatorMetricsPort := operatorPodsWithMetricsPort()
119-
for _, sspOperator := range operatorPods {
120-
sum += intMetricValue("kubevirt_ssp_operator_reconcile_succeeded", operatorMetricsPort, &sspOperator)
121-
}
122-
return
117+
func sspOperatorReconcileSucceededCount() (int, error) {
118+
return collectMetricFromOperator("kubevirt_ssp_operator_reconcile_succeeded")
119+
}
120+
121+
func totalRestoredTemplatesCount() (int, error) {
122+
return collectMetricFromOperator("kubevirt_ssp_common_templates_restored_total")
123123
}
124124

125-
func totalRestoredTemplatesCount() (sum int) {
125+
func collectMetricFromOperator(metricName string) (int, error) {
126126
operatorPods, operatorMetricsPort := operatorPodsWithMetricsPort()
127+
128+
var sum int
127129
for _, sspOperator := range operatorPods {
128-
sum += intMetricValue("kubevirt_ssp_common_templates_restored_total", operatorMetricsPort, &sspOperator)
130+
value, err := intMetricValue(metricName, operatorMetricsPort, &sspOperator)
131+
if err != nil {
132+
return 0, fmt.Errorf("failed to read int metric %s from pod %s: %w", metricName, sspOperator.Name, err)
133+
}
134+
sum += value
129135
}
130-
return
136+
137+
return sum, nil
131138
}
132139

133140
// Note we are not assuming only one operator pod here although that is how it should be,

tests/validator_test.go

+27-5
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,13 @@ func eventuallyFailToCreateVm(vm *kubevirtv1.VirtualMachine) bool {
941941
}
942942

943943
func failVmCreationToIncreaseRejectedVmsMetrics(template *templatev1.Template) {
944-
rejectedCountBefore := totalRejectedVmsMetricsValue()
944+
var rejectedCountBefore int
945+
Eventually(func() error {
946+
var err error
947+
rejectedCountBefore, err = totalRejectedVmsMetricsValue()
948+
return err
949+
}, env.ShortTimeout(), time.Second).Should(Succeed())
950+
945951
vmi := NewRandomVMIWithBridgeInterface(strategy.GetNamespace())
946952
// set values that will fail validation
947953
vmi = addDomainResourcesToVMI(vmi, 2, "test", "128M")
@@ -951,22 +957,38 @@ func failVmCreationToIncreaseRejectedVmsMetrics(template *templatev1.Template) {
951957
labels.AnnotationTemplateNamespaceKey: template.Namespace,
952958
}
953959
eventuallyFailToCreateVm(vm)
954-
Expect(totalRejectedVmsMetricsValue() - rejectedCountBefore).To(Equal(1))
960+
961+
var rejectedCountAfter int
962+
Eventually(func() error {
963+
var err error
964+
rejectedCountAfter, err = totalRejectedVmsMetricsValue()
965+
return err
966+
}, env.ShortTimeout(), time.Second).Should(Succeed())
967+
968+
Expect(rejectedCountAfter - rejectedCountBefore).To(Equal(1))
969+
955970
err := apiClient.Delete(ctx, vm)
956971
if !errors.IsNotFound(err) {
957972
Expect(err).ToNot(HaveOccurred(), "Failed to Delete VM")
958973
}
959974
waitForDeletion(client.ObjectKeyFromObject(vm), vm)
960975
}
961976

962-
func totalRejectedVmsMetricsValue() (sum int) {
977+
func totalRejectedVmsMetricsValue() (int, error) {
963978
pods, err := GetRunningPodsByLabel(validator.VirtTemplateValidator, validator.KubevirtIo, strategy.GetNamespace())
964979
Expect(err).ToNot(HaveOccurred(), "Could not find the validator pods")
965980
Expect(pods.Items).ToNot(BeEmpty())
981+
982+
var sum int
966983
for _, validatorPod := range pods.Items {
967-
sum += intMetricValue("kubevirt_ssp_template_validator_rejected_total", validator.MetricsPort, &validatorPod)
984+
const metricName = "kubevirt_ssp_template_validator_rejected_total"
985+
value, err := intMetricValue(metricName, validator.MetricsPort, &validatorPod)
986+
if err != nil {
987+
return 0, fmt.Errorf("failed to read int metric %s from pod %s: %w", metricName, validatorPod.Name, err)
988+
}
989+
sum += value
968990
}
969-
return
991+
return sum, nil
970992
}
971993

972994
func addObjectsToTemplates(genName, validation string) *templatev1.Template {

0 commit comments

Comments
 (0)