1
16
17 package metrics
18
19 import (
20 "strings"
21 "sync"
22 "testing"
23
24 "github.com/blang/semver/v4"
25 "github.com/prometheus/client_golang/prometheus"
26 "github.com/prometheus/client_golang/prometheus/testutil"
27 "github.com/stretchr/testify/assert"
28
29 apimachineryversion "k8s.io/apimachinery/pkg/version"
30 )
31
32 var (
33 v115 = semver.MustParse("1.15.0")
34 alphaCounter = NewCounter(
35 &CounterOpts{
36 Namespace: "some_namespace",
37 Name: "test_counter_name",
38 Subsystem: "subsystem",
39 StabilityLevel: ALPHA,
40 Help: "counter help",
41 },
42 )
43 alphaDeprecatedCounter = NewCounter(
44 &CounterOpts{
45 Namespace: "some_namespace",
46 Name: "test_alpha_dep_counter",
47 Subsystem: "subsystem",
48 StabilityLevel: ALPHA,
49 Help: "counter help",
50 DeprecatedVersion: "1.15.0",
51 },
52 )
53 alphaHiddenCounter = NewCounter(
54 &CounterOpts{
55 Namespace: "some_namespace",
56 Name: "test_alpha_hidden_counter",
57 Subsystem: "subsystem",
58 StabilityLevel: ALPHA,
59 Help: "counter help",
60 DeprecatedVersion: "1.14.0",
61 },
62 )
63 )
64
65 func TestShouldHide(t *testing.T) {
66 currentVersion := parseVersion(apimachineryversion.Info{
67 Major: "1",
68 Minor: "17",
69 GitVersion: "v1.17.1-alpha-1.12345",
70 })
71
72 var tests = []struct {
73 desc string
74 deprecatedVersion string
75 shouldHide bool
76 }{
77 {
78 desc: "current minor release should not be hidden",
79 deprecatedVersion: "1.17.0",
80 shouldHide: false,
81 },
82 {
83 desc: "older minor release should be hidden",
84 deprecatedVersion: "1.16.0",
85 shouldHide: true,
86 },
87 }
88
89 for _, test := range tests {
90 tc := test
91 t.Run(tc.desc, func(t *testing.T) {
92 result := shouldHide(¤tVersion, parseSemver(tc.deprecatedVersion))
93 assert.Equalf(t, tc.shouldHide, result, "expected should hide %v, but got %v", tc.shouldHide, result)
94 })
95 }
96 }
97
98 func TestRegister(t *testing.T) {
99 var tests = []struct {
100 desc string
101 metrics []*Counter
102 expectedErrors []error
103 expectedIsCreatedValues []bool
104 expectedIsDeprecated []bool
105 expectedIsHidden []bool
106 }{
107 {
108 desc: "test alpha metric",
109 metrics: []*Counter{alphaCounter},
110 expectedErrors: []error{nil},
111 expectedIsCreatedValues: []bool{true},
112 expectedIsDeprecated: []bool{false},
113 expectedIsHidden: []bool{false},
114 },
115 {
116 desc: "test registering same metric multiple times",
117 metrics: []*Counter{alphaCounter, alphaCounter},
118 expectedErrors: []error{nil, prometheus.AlreadyRegisteredError{}},
119 expectedIsCreatedValues: []bool{true, true},
120 expectedIsDeprecated: []bool{false, false},
121 expectedIsHidden: []bool{false, false},
122 },
123 {
124 desc: "test alpha deprecated metric",
125 metrics: []*Counter{alphaDeprecatedCounter},
126 expectedErrors: []error{nil},
127 expectedIsCreatedValues: []bool{true},
128 expectedIsDeprecated: []bool{true},
129 expectedIsHidden: []bool{false},
130 },
131 {
132 desc: "test alpha hidden metric",
133 metrics: []*Counter{alphaHiddenCounter},
134 expectedErrors: []error{nil},
135 expectedIsCreatedValues: []bool{false},
136 expectedIsDeprecated: []bool{true},
137 expectedIsHidden: []bool{true},
138 },
139 }
140
141 for _, test := range tests {
142 t.Run(test.desc, func(t *testing.T) {
143 registry := newKubeRegistry(apimachineryversion.Info{
144 Major: "1",
145 Minor: "15",
146 GitVersion: "v1.15.0-alpha-1.12345",
147 })
148 for i, m := range test.metrics {
149 err := registry.Register(m)
150 if err != nil && err.Error() != test.expectedErrors[i].Error() {
151 t.Errorf("Got unexpected error %v, wanted %v", err, test.expectedErrors[i])
152 }
153 if m.IsCreated() != test.expectedIsCreatedValues[i] {
154 t.Errorf("Got isCreated == %v, wanted isCreated to be %v", m.IsCreated(), test.expectedIsCreatedValues[i])
155 }
156 if m.IsDeprecated() != test.expectedIsDeprecated[i] {
157 t.Errorf("Got IsDeprecated == %v, wanted IsDeprecated to be %v", m.IsDeprecated(), test.expectedIsDeprecated[i])
158 }
159 if m.IsHidden() != test.expectedIsHidden[i] {
160 t.Errorf("Got IsHidden == %v, wanted IsHidden to be %v", m.IsHidden(), test.expectedIsDeprecated[i])
161 }
162 }
163 })
164 }
165 }
166
167 func TestMustRegister(t *testing.T) {
168 var tests = []struct {
169 desc string
170 metrics []*Counter
171 registryVersion *semver.Version
172 expectedPanics []bool
173 }{
174 {
175 desc: "test alpha metric",
176 metrics: []*Counter{alphaCounter},
177 registryVersion: &v115,
178 expectedPanics: []bool{false},
179 },
180 {
181 desc: "test registering same metric multiple times",
182 metrics: []*Counter{alphaCounter, alphaCounter},
183 registryVersion: &v115,
184 expectedPanics: []bool{false, true},
185 },
186 {
187 desc: "test alpha deprecated metric",
188 metrics: []*Counter{alphaDeprecatedCounter},
189 registryVersion: &v115,
190 expectedPanics: []bool{false},
191 },
192 {
193 desc: "test must registering same deprecated metric",
194 metrics: []*Counter{alphaDeprecatedCounter, alphaDeprecatedCounter},
195 registryVersion: &v115,
196 expectedPanics: []bool{false, true},
197 },
198 {
199 desc: "test alpha hidden metric",
200 metrics: []*Counter{alphaHiddenCounter},
201 registryVersion: &v115,
202 expectedPanics: []bool{false},
203 },
204 }
205
206 for _, test := range tests {
207 t.Run(test.desc, func(t *testing.T) {
208 registry := newKubeRegistry(apimachineryversion.Info{
209 Major: "1",
210 Minor: "15",
211 GitVersion: "v1.15.0-alpha-1.12345",
212 })
213 for i, m := range test.metrics {
214 if test.expectedPanics[i] {
215 assert.Panics(t,
216 func() { registry.MustRegister(m) },
217 "Did not panic even though we expected it.")
218 } else {
219 registry.MustRegister(m)
220 }
221 }
222 })
223 }
224
225 }
226 func TestShowHiddenMetric(t *testing.T) {
227 registry := newKubeRegistry(apimachineryversion.Info{
228 Major: "1",
229 Minor: "15",
230 GitVersion: "v1.15.0-alpha-1.12345",
231 })
232
233 expectedMetricCount := 0
234 registry.MustRegister(alphaHiddenCounter)
235
236 ms, err := registry.Gather()
237 assert.Nil(t, err, "Gather failed %v", err)
238 assert.Equalf(t, expectedMetricCount, len(ms), "Got %v metrics, Want: %v metrics", len(ms), expectedMetricCount)
239
240 showHidden.Store(true)
241 defer showHidden.Store(false)
242 registry.MustRegister(NewCounter(
243 &CounterOpts{
244 Namespace: "some_namespace",
245 Name: "test_alpha_show_hidden_counter",
246 Subsystem: "subsystem",
247 StabilityLevel: ALPHA,
248 Help: "counter help",
249 DeprecatedVersion: "1.14.0",
250 },
251 ))
252 expectedMetricCount = 1
253
254 ms, err = registry.Gather()
255 assert.Nil(t, err, "Gather failed %v", err)
256 assert.Equalf(t, expectedMetricCount, len(ms), "Got %v metrics, Want: %v metrics", len(ms), expectedMetricCount)
257 }
258
259 func TestValidateShowHiddenMetricsVersion(t *testing.T) {
260 currentVersion := parseVersion(apimachineryversion.Info{
261 Major: "1",
262 Minor: "17",
263 GitVersion: "v1.17.1-alpha-1.12345",
264 })
265
266 var tests = []struct {
267 desc string
268 targetVersion string
269 expectedError bool
270 }{
271 {
272 desc: "invalid version is not allowed",
273 targetVersion: "1.invalid",
274 expectedError: true,
275 },
276 {
277 desc: "patch version is not allowed",
278 targetVersion: "1.16.0",
279 expectedError: true,
280 },
281 {
282 desc: "old version is not allowed",
283 targetVersion: "1.15",
284 expectedError: true,
285 },
286 {
287 desc: "new version is not allowed",
288 targetVersion: "1.17",
289 expectedError: true,
290 },
291 {
292 desc: "valid version is allowed",
293 targetVersion: "1.16",
294 expectedError: false,
295 },
296 }
297
298 for _, test := range tests {
299 tc := test
300 t.Run(tc.desc, func(t *testing.T) {
301 err := validateShowHiddenMetricsVersion(currentVersion, tc.targetVersion)
302
303 if tc.expectedError {
304 assert.Errorf(t, err, "Failed to test: %s", tc.desc)
305 } else {
306 assert.NoErrorf(t, err, "Failed to test: %s", tc.desc)
307 }
308 })
309 }
310 }
311
312 func TestEnableHiddenMetrics(t *testing.T) {
313 currentVersion := apimachineryversion.Info{
314 Major: "1",
315 Minor: "17",
316 GitVersion: "v1.17.1-alpha-1.12345",
317 }
318
319 var tests = []struct {
320 name string
321 fqName string
322 counter *Counter
323 mustRegister bool
324 expectedMetric string
325 }{
326 {
327 name: "hide by register",
328 fqName: "hidden_metric_register",
329 counter: NewCounter(&CounterOpts{
330 Name: "hidden_metric_register",
331 Help: "counter help",
332 StabilityLevel: STABLE,
333 DeprecatedVersion: "1.16.0",
334 }),
335 mustRegister: false,
336 expectedMetric: `
337 # HELP hidden_metric_register [STABLE] (Deprecated since 1.16.0) counter help
338 # TYPE hidden_metric_register counter
339 hidden_metric_register 1
340 `,
341 },
342 {
343 name: "hide by must register",
344 fqName: "hidden_metric_must_register",
345 counter: NewCounter(&CounterOpts{
346 Name: "hidden_metric_must_register",
347 Help: "counter help",
348 StabilityLevel: STABLE,
349 DeprecatedVersion: "1.16.0",
350 }),
351 mustRegister: true,
352 expectedMetric: `
353 # HELP hidden_metric_must_register [STABLE] (Deprecated since 1.16.0) counter help
354 # TYPE hidden_metric_must_register counter
355 hidden_metric_must_register 1
356 `,
357 },
358 }
359
360 for _, test := range tests {
361 tc := test
362 t.Run(tc.name, func(t *testing.T) {
363 registry := newKubeRegistry(currentVersion)
364 if tc.mustRegister {
365 registry.MustRegister(tc.counter)
366 } else {
367 _ = registry.Register(tc.counter)
368 }
369
370 tc.counter.Inc()
371 if err := testutil.GatherAndCompare(registry, strings.NewReader(""), tc.fqName); err != nil {
372 t.Fatal(err)
373 }
374
375 SetShowHidden()
376 defer func() {
377 showHiddenOnce = *new(sync.Once)
378 showHidden.Store(false)
379 }()
380
381 tc.counter.Inc()
382 if err := testutil.GatherAndCompare(registry, strings.NewReader(tc.expectedMetric), tc.fqName); err != nil {
383 t.Fatal(err)
384 }
385 })
386 }
387 }
388
389 func TestEnableHiddenStableCollector(t *testing.T) {
390 var currentVersion = apimachineryversion.Info{
391 Major: "1",
392 Minor: "17",
393 GitVersion: "v1.17.0-alpha-1.12345",
394 }
395 var normal = NewDesc("test_enable_hidden_custom_metric_normal", "this is a normal metric", []string{"name"}, nil, STABLE, "")
396 var hiddenA = NewDesc("test_enable_hidden_custom_metric_hidden_a", "this is the hidden metric A", []string{"name"}, nil, STABLE, "1.16.0")
397 var hiddenB = NewDesc("test_enable_hidden_custom_metric_hidden_b", "this is the hidden metric B", []string{"name"}, nil, STABLE, "1.16.0")
398
399 var tests = []struct {
400 name string
401 descriptors []*Desc
402 metricNames []string
403 expectMetricsBeforeEnable string
404 expectMetricsAfterEnable string
405 }{
406 {
407 name: "all hidden",
408 descriptors: []*Desc{hiddenA, hiddenB},
409 metricNames: []string{"test_enable_hidden_custom_metric_hidden_a",
410 "test_enable_hidden_custom_metric_hidden_b"},
411 expectMetricsBeforeEnable: "",
412 expectMetricsAfterEnable: `
413 # HELP test_enable_hidden_custom_metric_hidden_a [STABLE] (Deprecated since 1.16.0) this is the hidden metric A
414 # TYPE test_enable_hidden_custom_metric_hidden_a gauge
415 test_enable_hidden_custom_metric_hidden_a{name="value"} 1
416 # HELP test_enable_hidden_custom_metric_hidden_b [STABLE] (Deprecated since 1.16.0) this is the hidden metric B
417 # TYPE test_enable_hidden_custom_metric_hidden_b gauge
418 test_enable_hidden_custom_metric_hidden_b{name="value"} 1
419 `,
420 },
421 {
422 name: "partial hidden",
423 descriptors: []*Desc{normal, hiddenA, hiddenB},
424 metricNames: []string{"test_enable_hidden_custom_metric_normal",
425 "test_enable_hidden_custom_metric_hidden_a",
426 "test_enable_hidden_custom_metric_hidden_b"},
427 expectMetricsBeforeEnable: `
428 # HELP test_enable_hidden_custom_metric_normal [STABLE] this is a normal metric
429 # TYPE test_enable_hidden_custom_metric_normal gauge
430 test_enable_hidden_custom_metric_normal{name="value"} 1
431 `,
432 expectMetricsAfterEnable: `
433 # HELP test_enable_hidden_custom_metric_normal [STABLE] this is a normal metric
434 # TYPE test_enable_hidden_custom_metric_normal gauge
435 test_enable_hidden_custom_metric_normal{name="value"} 1
436 # HELP test_enable_hidden_custom_metric_hidden_a [STABLE] (Deprecated since 1.16.0) this is the hidden metric A
437 # TYPE test_enable_hidden_custom_metric_hidden_a gauge
438 test_enable_hidden_custom_metric_hidden_a{name="value"} 1
439 # HELP test_enable_hidden_custom_metric_hidden_b [STABLE] (Deprecated since 1.16.0) this is the hidden metric B
440 # TYPE test_enable_hidden_custom_metric_hidden_b gauge
441 test_enable_hidden_custom_metric_hidden_b{name="value"} 1
442 `,
443 },
444 }
445
446 for _, test := range tests {
447 tc := test
448 t.Run(tc.name, func(t *testing.T) {
449 registry := newKubeRegistry(currentVersion)
450 customCollector := newTestCustomCollector(tc.descriptors...)
451 registry.CustomMustRegister(customCollector)
452
453 if err := testutil.GatherAndCompare(registry, strings.NewReader(tc.expectMetricsBeforeEnable), tc.metricNames...); err != nil {
454 t.Fatalf("before enable test failed: %v", err)
455 }
456
457 SetShowHidden()
458 defer func() {
459 showHiddenOnce = *new(sync.Once)
460 showHidden.Store(false)
461 }()
462
463 if err := testutil.GatherAndCompare(registry, strings.NewReader(tc.expectMetricsAfterEnable), tc.metricNames...); err != nil {
464 t.Fatalf("after enable test failed: %v", err)
465 }
466
467
468 for _, d := range tc.descriptors {
469 d.ClearState()
470 }
471 })
472 }
473 }
474
475 func TestRegistryReset(t *testing.T) {
476 currentVersion := apimachineryversion.Info{
477 Major: "1",
478 Minor: "17",
479 GitVersion: "v1.17.1-alpha-1.12345",
480 }
481 registry := newKubeRegistry(currentVersion)
482 resettableMetric := NewCounterVec(&CounterOpts{
483 Name: "reset_metric",
484 Help: "this metric can be reset",
485 }, []string{"label"})
486
487 nonResettableMetric := NewGauge(&GaugeOpts{
488 Name: "not_reset_metric",
489 Help: "this metric cannot be reset",
490 })
491
492 registry.MustRegister(resettableMetric)
493 registry.MustRegister(nonResettableMetric)
494 resettableMetric.WithLabelValues("one").Inc()
495 resettableMetric.WithLabelValues("two").Inc()
496 resettableMetric.WithLabelValues("two").Inc()
497 nonResettableMetric.Inc()
498
499 nonResettableOutput := `
500 # HELP not_reset_metric [ALPHA] this metric cannot be reset
501 # TYPE not_reset_metric gauge
502 not_reset_metric 1
503 `
504 resettableOutput := `
505 # HELP reset_metric [ALPHA] this metric can be reset
506 # TYPE reset_metric counter
507 reset_metric{label="one"} 1
508 reset_metric{label="two"} 2
509 `
510 if err := testutil.GatherAndCompare(registry, strings.NewReader(nonResettableOutput+resettableOutput), "reset_metric", "not_reset_metric"); err != nil {
511 t.Fatal(err)
512 }
513 registry.Reset()
514 if err := testutil.GatherAndCompare(registry, strings.NewReader(nonResettableOutput), "reset_metric", "not_reset_metric"); err != nil {
515 t.Fatal(err)
516 }
517 }
518
519 func TestDisabledMetrics(t *testing.T) {
520 o := NewOptions()
521 o.DisabledMetrics = []string{"should_be_disabled"}
522 o.Apply()
523 currentVersion := apimachineryversion.Info{
524 Major: "1",
525 Minor: "17",
526 GitVersion: "v1.17.1-alpha-1.12345",
527 }
528 registry := newKubeRegistry(currentVersion)
529 disabledMetric := NewCounterVec(&CounterOpts{
530 Name: "should_be_disabled",
531 Help: "this metric should be disabled",
532 }, []string{"label"})
533
534 enabledMetric := NewGauge(&GaugeOpts{
535 Name: "should_be_enabled",
536 Help: "this metric should not be disabled",
537 })
538
539 registry.MustRegister(disabledMetric)
540 registry.MustRegister(enabledMetric)
541 disabledMetric.WithLabelValues("one").Inc()
542 disabledMetric.WithLabelValues("two").Inc()
543 disabledMetric.WithLabelValues("two").Inc()
544 enabledMetric.Inc()
545
546 enabledMetricOutput := `
547 # HELP should_be_enabled [ALPHA] this metric should not be disabled
548 # TYPE should_be_enabled gauge
549 should_be_enabled 1
550 `
551
552 if err := testutil.GatherAndCompare(registry, strings.NewReader(enabledMetricOutput), "should_be_disabled", "should_be_enabled"); err != nil {
553 t.Fatal(err)
554 }
555 }
556
View as plain text