1
16
17 package main
18
19 import (
20 "fmt"
21 "reflect"
22 "testing"
23
24 "github.com/google/go-cmp/cmp"
25
26 "k8s.io/component-base/metrics"
27 )
28
29 const fakeFilename = "testdata/metric.go"
30
31 func TestSkipMetrics(t *testing.T) {
32 for _, test := range []struct {
33 testName string
34 src string
35 }{
36 {
37 testName: "Skip alpha metric with local variable",
38 src: `
39 package test
40 import "k8s.io/component-base/metrics"
41 var name = "metric"
42 var _ = metrics.NewCounter(
43 &metrics.CounterOpts{
44 Name: name,
45 StabilityLevel: metrics.ALPHA,
46 },
47 )
48 `},
49 {
50 testName: "Skip alpha metric created via function call",
51 src: `
52 package test
53 import "k8s.io/component-base/metrics"
54 func getName() string {
55 return "metric"
56 }
57 var _ = metrics.NewCounter(
58 &metrics.CounterOpts{
59 Name: getName(),
60 StabilityLevel: metrics.ALPHA,
61 },
62 )
63 `},
64 {
65 testName: "Skip metric without stability set",
66 src: `
67 package test
68 import "k8s.io/component-base/metrics"
69 var _ = metrics.NewCounter(
70 &metrics.CounterOpts{
71 Name: "metric",
72 },
73 )
74 `},
75 {
76 testName: "Skip functions of similar signature (not imported from framework path) with import rename",
77 src: `
78 package test
79 import metrics "k8s.io/fake/path"
80 var _ = metrics.NewCounter(
81 &metrics.CounterOpts{
82 StabilityLevel: metrics.STABLE,
83 },
84 )
85 `},
86 {
87 testName: "Skip functions of similar signature (not imported from framework path)",
88 src: `
89 package test
90 import "k8s.io/fake/path/metrics"
91 var _ = metrics.NewCounter(
92 &metrics.CounterOpts{
93 StabilityLevel: metrics.STABLE,
94 },
95 )
96 `},
97 {
98 testName: "Skip . package import of non metric framework",
99 src: `
100 package test
101 import . "k8s.io/fake/path"
102 var _ = NewCounter(
103 &CounterOpts{
104 StabilityLevel: STABLE,
105 },
106 )
107 `},
108 } {
109 t.Run(test.testName, func(t *testing.T) {
110 metrics, errors := searchFileForStableMetrics(fakeFilename, test.src)
111 if len(metrics) != 0 {
112 t.Errorf("Didn't expect any stable metrics found, got: %d", len(metrics))
113 }
114 if len(errors) != 0 {
115 t.Errorf("Didn't expect any errors found, got: %s", errors)
116 }
117 })
118 }
119 }
120
121 func TestStableMetric(t *testing.T) {
122 for _, test := range []struct {
123 testName string
124 src string
125 metric metric
126 }{
127 {
128 testName: "Counter",
129 metric: metric{
130 Name: "metric",
131 Namespace: "namespace",
132 Subsystem: "subsystem",
133 StabilityLevel: "STABLE",
134 DeprecatedVersion: "1.16",
135 Help: "help",
136 Type: counterMetricType,
137 },
138 src: `
139 package test
140 import "k8s.io/component-base/metrics"
141 var _ = metrics.NewCounter(
142 &metrics.CounterOpts{
143 Name: "metric",
144 Subsystem: "subsystem",
145 Namespace: "namespace",
146 Help: "help",
147 DeprecatedVersion: "1.16",
148 StabilityLevel: metrics.STABLE,
149 },
150 )
151 `},
152 {
153 testName: "CounterVec",
154 metric: metric{
155 Name: "metric",
156 Namespace: "namespace",
157 Subsystem: "subsystem",
158 Labels: []string{"label-1"},
159 StabilityLevel: "STABLE",
160 DeprecatedVersion: "1.16",
161 Help: "help",
162 Type: counterMetricType,
163 },
164 src: `
165 package test
166 import "k8s.io/component-base/metrics"
167 var _ = metrics.NewCounterVec(
168 &metrics.CounterOpts{
169 Name: "metric",
170 Namespace: "namespace",
171 Subsystem: "subsystem",
172 Help: "help",
173 DeprecatedVersion: "1.16",
174 StabilityLevel: metrics.STABLE,
175 },
176 []string{"label-1"},
177 )
178 `},
179 {
180 testName: "Gauge",
181 metric: metric{
182 Name: "gauge",
183 Namespace: "namespace",
184 Subsystem: "subsystem",
185 StabilityLevel: "STABLE",
186 DeprecatedVersion: "1.16",
187 Help: "help",
188 Type: gaugeMetricType,
189 },
190 src: `
191 package test
192 import "k8s.io/component-base/metrics"
193 var _ = metrics.NewGauge(
194 &metrics.GaugeOpts{
195 Name: "gauge",
196 Namespace: "namespace",
197 Subsystem: "subsystem",
198 Help: "help",
199 DeprecatedVersion: "1.16",
200 StabilityLevel: metrics.STABLE,
201 },
202 )
203 `},
204 {
205 testName: "GaugeVec",
206 metric: metric{
207 Name: "gauge",
208 Namespace: "namespace",
209 Subsystem: "subsystem",
210 StabilityLevel: "STABLE",
211 DeprecatedVersion: "1.16",
212 Help: "help",
213 Type: gaugeMetricType,
214 Labels: []string{"label-1", "label-2"},
215 },
216 src: `
217 package test
218 import "k8s.io/component-base/metrics"
219 var _ = metrics.NewGaugeVec(
220 &metrics.GaugeOpts{
221 Name: "gauge",
222 Namespace: "namespace",
223 Subsystem: "subsystem",
224 Help: "help",
225 DeprecatedVersion: "1.16",
226 StabilityLevel: metrics.STABLE,
227 },
228 []string{"label-2", "label-1"},
229 )
230 `},
231 {
232 testName: "Histogram",
233 metric: metric{
234 Name: "histogram",
235 Namespace: "namespace",
236 Subsystem: "subsystem",
237 DeprecatedVersion: "1.16",
238 StabilityLevel: "STABLE",
239 Buckets: []float64{0.001, 0.01, 0.1, 1, 10, 100},
240 Help: "help",
241 Type: histogramMetricType,
242 },
243 src: `
244 package test
245 import "k8s.io/component-base/metrics"
246 var _ = metrics.NewHistogram(
247 &metrics.HistogramOpts{
248 Name: "histogram",
249 Namespace: "namespace",
250 Subsystem: "subsystem",
251 StabilityLevel: metrics.STABLE,
252 Help: "help",
253 DeprecatedVersion: "1.16",
254 Buckets: []float64{0.001, 0.01, 0.1, 1, 10, 100},
255 },
256 )
257 `},
258 {
259 testName: "HistogramVec",
260 metric: metric{
261 Name: "histogram",
262 Namespace: "namespace",
263 Subsystem: "subsystem",
264 DeprecatedVersion: "1.16",
265 StabilityLevel: "STABLE",
266 Buckets: []float64{0.001, 0.01, 0.1, 1, 10, 100},
267 Help: "help",
268 Type: histogramMetricType,
269 Labels: []string{"label-1", "label-2"},
270 },
271 src: `
272 package test
273 import "k8s.io/component-base/metrics"
274 var _ = metrics.NewHistogramVec(
275 &metrics.HistogramOpts{
276 Name: "histogram",
277 Namespace: "namespace",
278 Subsystem: "subsystem",
279 StabilityLevel: metrics.STABLE,
280 Help: "help",
281 DeprecatedVersion: "1.16",
282 Buckets: []float64{0.001, 0.01, 0.1, 1, 10, 100},
283 },
284 []string{"label-2", "label-1"},
285 )
286 `},
287 {
288 testName: "Custom import",
289 metric: metric{
290 Name: "metric",
291 StabilityLevel: "STABLE",
292 Type: counterMetricType,
293 },
294 src: `
295 package test
296 import custom "k8s.io/component-base/metrics"
297 var _ = custom.NewCounter(
298 &custom.CounterOpts{
299 Name: "metric",
300 StabilityLevel: custom.STABLE,
301 },
302 )
303 `},
304 {
305 testName: "Custom import NewDesc",
306 metric: metric{
307 Name: "apiserver_storage_size_bytes",
308 Help: "Size of the storage database file physically allocated in bytes.",
309 Labels: []string{"server"},
310 StabilityLevel: "STABLE",
311 Type: customType,
312 ConstLabels: map[string]string{},
313 },
314 src: `
315 package test
316 import custom "k8s.io/component-base/metrics"
317 var _ = custom.NewDesc("apiserver_storage_size_bytes", "Size of the storage database file physically allocated in bytes.", []string{"server"}, nil, custom.STABLE, "")
318 `},
319 {
320 testName: "Const",
321 metric: metric{
322 Name: "metric",
323 StabilityLevel: "STABLE",
324 Type: counterMetricType,
325 },
326 src: `
327 package test
328 import "k8s.io/component-base/metrics"
329 const name = "metric"
330 var _ = metrics.NewCounter(
331 &metrics.CounterOpts{
332 Name: name,
333 StabilityLevel: metrics.STABLE,
334 },
335 )
336 `},
337 {
338 testName: "Variable",
339 metric: metric{
340 Name: "metric",
341 StabilityLevel: "STABLE",
342 Type: counterMetricType,
343 },
344 src: `
345 package test
346 import "k8s.io/component-base/metrics"
347 var name = "metric"
348 var _ = metrics.NewCounter(
349 &metrics.CounterOpts{
350 Name: name,
351 StabilityLevel: metrics.STABLE,
352 },
353 )
354 `},
355 {
356 testName: "Multiple consts in block",
357 metric: metric{
358 Name: "metric",
359 StabilityLevel: "STABLE",
360 Type: counterMetricType,
361 },
362 src: `
363 package test
364 import "k8s.io/component-base/metrics"
365 const (
366 unrelated1 = "unrelated1"
367 name = "metric"
368 unrelated2 = "unrelated2"
369 )
370 var _ = metrics.NewCounter(
371 &metrics.CounterOpts{
372 Name: name,
373 StabilityLevel: metrics.STABLE,
374 },
375 )
376 `},
377 {
378 testName: "Multiple variables in Block",
379 metric: metric{
380 Name: "metric",
381 StabilityLevel: "STABLE",
382 Type: counterMetricType,
383 },
384 src: `
385 package test
386 import "k8s.io/component-base/metrics"
387 var (
388 unrelated1 = "unrelated1"
389 name = "metric"
390 _ = metrics.NewCounter(
391 &metrics.CounterOpts{
392 Name: name,
393 StabilityLevel: metrics.STABLE,
394 },
395 )
396 )
397 `},
398 {
399 testName: "Histogram with linear buckets",
400 metric: metric{
401 Name: "histogram",
402 StabilityLevel: "STABLE",
403 Buckets: metrics.LinearBuckets(1, 1, 3),
404 Type: histogramMetricType,
405 },
406 src: `
407 package test
408 import "k8s.io/component-base/metrics"
409 var _ = metrics.NewHistogram(
410 &metrics.HistogramOpts{
411 Name: "histogram",
412 StabilityLevel: metrics.STABLE,
413 Buckets: metrics.LinearBuckets(1, 1, 3),
414 },
415 )
416 `},
417 {
418 testName: "Histogram with exponential buckets",
419 metric: metric{
420 Name: "histogram",
421 StabilityLevel: "STABLE",
422 Buckets: metrics.ExponentialBuckets(1, 2, 3),
423 Type: histogramMetricType,
424 },
425 src: `
426 package test
427 import "k8s.io/component-base/metrics"
428 var _ = metrics.NewHistogram(
429 &metrics.HistogramOpts{
430 Name: "histogram",
431 StabilityLevel: metrics.STABLE,
432 Buckets: metrics.ExponentialBuckets(1, 2, 3),
433 },
434 )
435 `},
436 {
437 testName: "Histogram with default buckets",
438 metric: metric{
439 Name: "histogram",
440 StabilityLevel: "STABLE",
441 Buckets: metrics.DefBuckets,
442 Type: histogramMetricType,
443 },
444 src: `
445 package test
446 import "k8s.io/component-base/metrics"
447 var _ = metrics.NewHistogram(
448 &metrics.HistogramOpts{
449 Name: "histogram",
450 StabilityLevel: metrics.STABLE,
451 Buckets: metrics.DefBuckets,
452 },
453 )
454 `},
455 {
456 testName: "Imported k8s.io constant",
457 metric: metric{
458 Name: "importedCounter",
459 StabilityLevel: "STABLE",
460 Subsystem: "kubelet",
461 Type: counterMetricType,
462 },
463 src: `
464 package test
465 import compbasemetrics "k8s.io/component-base/metrics"
466 import "k8s.io/kubernetes/pkg/kubelet/metrics"
467 var _ = compbasemetrics.NewCounter(
468 &compbasemetrics.CounterOpts{
469 Name: "importedCounter",
470 StabilityLevel: compbasemetrics.STABLE,
471 Subsystem: metrics.KubeletSubsystem,
472 },
473 )
474 `},
475 } {
476 t.Run(test.testName, func(t *testing.T) {
477 metrics, errors := searchFileForStableMetrics(fakeFilename, test.src)
478 if len(errors) != 0 {
479 t.Errorf("Unexpected errors: %s", errors)
480 }
481 if len(metrics) != 1 {
482 t.Fatalf("Unexpected number of metrics: got %d, want 1", len(metrics))
483 }
484 if test.metric.Labels == nil {
485 test.metric.Labels = []string{}
486 }
487 if diff := cmp.Diff(metrics[0], test.metric); diff != "" {
488 t.Errorf("metric diff: %s", diff)
489 }
490 })
491 }
492 }
493
494 func TestIncorrectStableMetricDeclarations(t *testing.T) {
495 for _, test := range []struct {
496 testName string
497 src string
498 err error
499 }{
500 {
501 testName: "Fail on stable metric with attribute set to unknown variable",
502 err: fmt.Errorf("testdata/metric.go:6:4: Metric attribute was not correctly set. Please use only global consts in same file"),
503 src: `
504 package test
505 import "k8s.io/component-base/metrics"
506 var _ = metrics.NewCounter(
507 &metrics.CounterOpts{
508 Name: unknownVariable,
509 StabilityLevel: metrics.STABLE,
510 },
511 )
512 `},
513 {
514 testName: "Fail on stable metric with attribute set to local function return",
515 err: fmt.Errorf("testdata/metric.go:9:4: Non string attribute is not supported"),
516 src: `
517 package test
518 import "k8s.io/component-base/metrics"
519 func getName() string {
520 return "metric"
521 }
522 var _ = metrics.NewCounter(
523 &metrics.CounterOpts{
524 Name: getName(),
525 StabilityLevel: metrics.STABLE,
526 },
527 )
528 `},
529 {
530 testName: "Fail on stable metric with attribute set to imported function return",
531 err: fmt.Errorf("testdata/metric.go:7:4: Non string attribute is not supported"),
532 src: `
533 package test
534 import "k8s.io/component-base/metrics"
535 import "os"
536 var _ = metrics.NewCounter(
537 &metrics.CounterOpts{
538 Name: os.Getenv("name"), // any imported function will do
539 StabilityLevel: metrics.STABLE,
540 },
541 )
542 `},
543 {
544 testName: "Fail on metric with stability set to function return",
545 err: fmt.Errorf("testdata/metric.go:9:20: %s", errStabilityLevel),
546 src: `
547 package test
548 import "k8s.io/component-base/metrics"
549 func getMetricStability() metrics.StabilityLevel {
550 return metrics.STABLE
551 }
552 var _ = metrics.NewCounter(
553 &metrics.CounterOpts{
554 StabilityLevel: getMetricsStability(),
555 },
556 )
557 `},
558 {
559 testName: "error for passing stability as string",
560 err: fmt.Errorf("testdata/metric.go:6:20: %s", errStabilityLevel),
561 src: `
562 package test
563 import "k8s.io/component-base/metrics"
564 var _ = metrics.NewCounter(
565 &metrics.CounterOpts{
566 StabilityLevel: "stable",
567 },
568 )
569 `},
570 {
571 testName: "error for passing stability as variable",
572 err: fmt.Errorf("testdata/metric.go:7:20: %s", errStabilityLevel),
573 src: `
574 package test
575 import "k8s.io/component-base/metrics"
576 var stable = metrics.STABLE
577 var _ = metrics.NewCounter(
578 &metrics.CounterOpts{
579 StabilityLevel: stable,
580 },
581 )
582 `},
583 {
584 testName: "error for stable metric created via function call",
585 err: fmt.Errorf("testdata/metric.go:6:10: Opts for STABLE metric was not directly passed to new metric function"),
586 src: `
587 package test
588 import "k8s.io/component-base/metrics"
589 var _ = metrics.NewCounter(getStableCounterOpts())
590 func getStableCounterOpts() *metrics.CounterOpts {
591 return &metrics.CounterOpts{
592 StabilityLevel: metrics.STABLE,
593 }
594 }
595 `},
596 {
597 testName: "error . package import of metric framework",
598 err: fmt.Errorf(`testdata/metric.go:3:8: Importing using "." is not supported`),
599 src: `
600 package test
601 import . "k8s.io/component-base/metrics"
602 var _ = NewCounter(
603 &CounterOpts{
604 StabilityLevel: STABLE,
605 },
606 )
607 `},
608 {
609 testName: "error stable metric opts passed to local function",
610 err: fmt.Errorf("testdata/metric.go:4:9: Opts for STABLE metric was not directly passed to new metric function"),
611 src: `
612 package test
613 import "k8s.io/component-base/metrics"
614 var _ = RegisterMetric(
615 &metrics.CounterOpts{
616 StabilityLevel: metrics.STABLE,
617 },
618 )
619 `},
620 {
621 testName: "error stable metric opts passed to imported function",
622 err: fmt.Errorf("testdata/metric.go:6:4: Positional arguments are not supported"),
623 src: `
624 package test
625 import "k8s.io/component-base/metrics"
626 var _ = metrics.NewCounter(
627 &metrics.CounterOpts{
628 "counter",
629 },
630 )
631 `},
632 {
633 testName: "error stable histogram with unknown prometheus bucket variable",
634 err: fmt.Errorf("testdata/metric.go:9:13: Buckets should be set to list of floats, result from function call of prometheus.LinearBuckets or prometheus.ExponentialBuckets"),
635 src: `
636 package test
637 import "k8s.io/component-base/metrics"
638 import "github.com/prometheus/client_golang/prometheus"
639 var _ = metrics.NewHistogram(
640 &metrics.HistogramOpts{
641 Name: "histogram",
642 StabilityLevel: metrics.STABLE,
643 Buckets: prometheus.FakeBuckets,
644 },
645 )
646 `},
647 {
648 testName: "error stable summary with unknown prometheus objective variable",
649 err: fmt.Errorf("testdata/metric.go:9:16: Objectives should be set to map of floats to floats"),
650 src: `
651 package test
652 import "k8s.io/component-base/metrics"
653 import "github.com/prometheus/client_golang/prometheus"
654 var _ = metrics.NewSummary(
655 &metrics.SummaryOpts{
656 Name: "summary",
657 StabilityLevel: metrics.STABLE,
658 Objectives: prometheus.FakeObjectives,
659 },
660 )
661 `},
662 {
663 testName: "error stable historgram with unknown bucket variable from unknown library",
664 err: fmt.Errorf("testdata/metric.go:9:13: Buckets should be set to list of floats, result from function call of prometheus.LinearBuckets or prometheus.ExponentialBuckets"),
665 src: `
666 package test
667 import "k8s.io/component-base/metrics"
668 import "github.com/prometheus/client_golang/prometheus"
669 var _ = metrics.NewHistogram(
670 &metrics.HistogramOpts{
671 Name: "histogram",
672 StabilityLevel: metrics.STABLE,
673 Buckets: prometheus.DefBuckets,
674 },
675 )
676 `},
677 } {
678 t.Run(test.testName, func(t *testing.T) {
679 _, errors := searchFileForStableMetrics(fakeFilename, test.src)
680 if len(errors) != 1 {
681 t.Errorf("Unexpected number of errors, got %d, want 1", len(errors))
682 }
683 if !reflect.DeepEqual(errors[0], test.err) {
684 t.Errorf("error:\ngot %v\nwant %v", errors[0], test.err)
685 }
686 })
687 }
688 }
689
View as plain text