cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.57.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/parser/parser_test.go

1848lines · modecode

1package parser_test
2
3import (
4 "fmt"
5 "strconv"
6 "testing"
7
8 "github.com/stretchr/testify/require"
9
10 "github.com/cloudflare/pint/internal/comments"
11 "github.com/cloudflare/pint/internal/parser"
12
13 "github.com/google/go-cmp/cmp"
14)
15
16func TestParse(t *testing.T) {
17 type testCaseT struct {
18 err string
19 content []byte
20 output []parser.Rule
21 }
22
23 testCases := []testCaseT{
24 {
25 content: nil,
26 output: nil,
27 },
28 {
29 content: []byte{},
30 output: nil,
31 },
32 {
33 content: []byte(string("! !00 \xf6")),
34 output: nil,
35 err: "yaml: incomplete UTF-8 octet sequence",
36 },
37 {
38 content: []byte("- 0: 0\n 00000000: 000000\n 000000:00000000000: 00000000\n 00000000000:000000: 0000000000000000000000000000000000\n 000000: 0000000\n expr: |"),
39 output: []parser.Rule{
40 {
41 Lines: parser.LineRange{First: 1, Last: 6},
42 Error: parser.ParseError{Err: fmt.Errorf("incomplete rule, no alert or record key"), Line: 6},
43 },
44 },
45 },
46 {
47 content: []byte("- record: |\n multiline\n"),
48 output: []parser.Rule{
49 {
50 Lines: parser.LineRange{First: 1, Last: 2},
51 Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 2},
52 },
53 },
54 },
55 {
56 content: []byte("- expr: foo\n"),
57 output: []parser.Rule{
58 {
59 Lines: parser.LineRange{First: 1, Last: 1},
60 Error: parser.ParseError{Err: fmt.Errorf("incomplete rule, no alert or record key"), Line: 1},
61 },
62 },
63 },
64 {
65 content: []byte("- alert: foo\n"),
66 output: []parser.Rule{
67 {
68 Lines: parser.LineRange{First: 1, Last: 1},
69 Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 1},
70 },
71 },
72 },
73 {
74 content: []byte("- alert: foo\n record: foo\n"),
75 output: []parser.Rule{
76 {
77 Lines: parser.LineRange{First: 1, Last: 2},
78 Error: parser.ParseError{Err: fmt.Errorf("got both record and alert keys in a single rule"), Line: 1},
79 },
80 },
81 },
82 {
83 content: []byte("- record: foo\n labels:\n foo: bar\n"),
84 output: []parser.Rule{
85 {
86 Lines: parser.LineRange{First: 1, Last: 3},
87 Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 1},
88 },
89 },
90 },
91 {
92 content: []byte("- record: - foo\n"),
93 err: "yaml: block sequence entries are not allowed in this context",
94 },
95 {
96 content: []byte("- record: foo expr: sum(\n"),
97 err: "yaml: mapping values are not allowed in this context",
98 },
99 {
100 content: []byte("- record\n\texpr: foo\n"),
101 err: "yaml: line 2: found a tab character that violates indentation",
102 },
103 {
104 content: []byte(`
105- record: foo
106 expr: bar
107 expr: bar
108`),
109 output: []parser.Rule{
110 {
111 Lines: parser.LineRange{First: 2, Last: 4},
112 Error: parser.ParseError{Err: fmt.Errorf("duplicated expr key"), Line: 4},
113 },
114 },
115 },
116 {
117 content: []byte(`
118- record: foo
119 expr: bar
120 record: bar
121`),
122 output: []parser.Rule{
123 {
124 Lines: parser.LineRange{First: 2, Last: 4},
125 Error: parser.ParseError{Err: fmt.Errorf("duplicated record key"), Line: 4},
126 },
127 },
128 },
129 {
130 content: []byte(`
131- alert: foo
132 alert: bar
133 expr: bar
134`),
135 output: []parser.Rule{
136 {
137 Lines: parser.LineRange{First: 2, Last: 3},
138 Error: parser.ParseError{Err: fmt.Errorf("duplicated alert key"), Line: 3},
139 },
140 },
141 },
142 {
143 content: []byte(`
144- alert: foo
145 for: 5m
146 expr: bar
147 for: 1m
148`),
149 output: []parser.Rule{
150 {
151 Lines: parser.LineRange{First: 2, Last: 5},
152 Error: parser.ParseError{Err: fmt.Errorf("duplicated for key"), Line: 5},
153 },
154 },
155 },
156 {
157 content: []byte(`
158- alert: foo
159 keep_firing_for: 5m
160 expr: bar
161 keep_firing_for: 1m
162`),
163 output: []parser.Rule{
164 {
165 Lines: parser.LineRange{First: 2, Last: 5},
166 Error: parser.ParseError{Err: fmt.Errorf("duplicated keep_firing_for key"), Line: 5},
167 },
168 },
169 },
170 {
171 content: []byte(`
172- alert: foo
173 labels: {}
174 expr: bar
175 labels: {}
176`),
177 output: []parser.Rule{
178 {
179 Lines: parser.LineRange{First: 2, Last: 5},
180 Error: parser.ParseError{Err: fmt.Errorf("duplicated labels key"), Line: 5},
181 },
182 },
183 },
184 {
185 content: []byte(`
186- record: foo
187 labels: {}
188 expr: bar
189 labels: {}
190`),
191 output: []parser.Rule{
192 {
193 Lines: parser.LineRange{First: 2, Last: 5},
194 Error: parser.ParseError{Err: fmt.Errorf("duplicated labels key"), Line: 5},
195 },
196 },
197 },
198 {
199 content: []byte(`
200- alert: foo
201 annotations: {}
202 expr: bar
203 annotations: {}
204`),
205 output: []parser.Rule{
206 {
207 Lines: parser.LineRange{First: 2, Last: 5},
208 Error: parser.ParseError{Err: fmt.Errorf("duplicated annotations key"), Line: 5},
209 },
210 },
211 },
212 {
213 content: []byte("- record: foo\n expr: foo\n extra: true\n"),
214 output: []parser.Rule{
215 {
216 Lines: parser.LineRange{First: 1, Last: 3},
217 Error: parser.ParseError{Err: fmt.Errorf("invalid key(s) found: extra"), Line: 3},
218 },
219 },
220 },
221 {
222 content: []byte("- record: foo\n expr: foo offset 10m\n"),
223 output: []parser.Rule{
224 {
225 Lines: parser.LineRange{First: 1, Last: 2},
226 RecordingRule: &parser.RecordingRule{
227 Record: parser.YamlNode{
228 Lines: parser.LineRange{First: 1, Last: 1},
229 Value: "foo",
230 },
231 Expr: parser.PromQLExpr{
232 Value: &parser.YamlNode{
233 Lines: parser.LineRange{First: 2, Last: 2},
234 Value: "foo offset 10m",
235 },
236 },
237 },
238 },
239 },
240 },
241 {
242 content: []byte("- record: foo\n expr: foo offset -10m\n"),
243 output: []parser.Rule{
244 {
245 Lines: parser.LineRange{First: 1, Last: 2},
246 RecordingRule: &parser.RecordingRule{
247 Record: parser.YamlNode{
248 Lines: parser.LineRange{First: 1, Last: 1},
249 Value: "foo",
250 },
251 Expr: parser.PromQLExpr{
252 Value: &parser.YamlNode{
253 Lines: parser.LineRange{First: 2, Last: 2},
254 Value: "foo offset -10m",
255 },
256 },
257 },
258 },
259 },
260 },
261 {
262 content: []byte(`
263# pint disable head comment
264- record: foo # pint disable record comment
265 expr: foo offset 10m # pint disable expr comment
266 # pint disable pre-labels comment
267 labels:
268 # pint disable pre-foo comment
269 foo: bar
270 # pint disable post-foo comment
271 bob: alice
272 # pint disable foot comment
273`),
274 output: []parser.Rule{
275 {
276 Lines: parser.LineRange{First: 3, Last: 10},
277 Comments: []comments.Comment{
278 {
279 Type: comments.DisableType,
280 Value: comments.Disable{Match: "head comment"},
281 },
282 {
283 Type: comments.DisableType,
284 Value: comments.Disable{Match: "record comment"},
285 },
286 {
287 Type: comments.DisableType,
288 Value: comments.Disable{Match: "expr comment"},
289 },
290 {
291 Type: comments.DisableType,
292 Value: comments.Disable{Match: "pre-labels comment"},
293 },
294 {
295 Type: comments.DisableType,
296 Value: comments.Disable{Match: "foot comment"},
297 },
298 {
299 Type: comments.DisableType,
300 Value: comments.Disable{Match: "pre-foo comment"},
301 },
302 {
303 Type: comments.DisableType,
304 Value: comments.Disable{Match: "post-foo comment"},
305 },
306 },
307 RecordingRule: &parser.RecordingRule{
308 Record: parser.YamlNode{
309 Lines: parser.LineRange{First: 3, Last: 3},
310 Value: "foo",
311 },
312 Expr: parser.PromQLExpr{
313 Value: &parser.YamlNode{
314 Lines: parser.LineRange{First: 4, Last: 4},
315 Value: "foo offset 10m",
316 },
317 },
318 Labels: &parser.YamlMap{
319 Lines: parser.LineRange{First: 6, Last: 10},
320 Key: &parser.YamlNode{
321 Lines: parser.LineRange{First: 6, Last: 6},
322 Value: "labels",
323 },
324 Items: []*parser.YamlKeyValue{
325 {
326 Key: &parser.YamlNode{
327 Lines: parser.LineRange{First: 8, Last: 8},
328 Value: "foo",
329 },
330 Value: &parser.YamlNode{
331 Lines: parser.LineRange{First: 8, Last: 8},
332 Value: "bar",
333 },
334 },
335 {
336 Key: &parser.YamlNode{
337 Lines: parser.LineRange{First: 10, Last: 10},
338 Value: "bob",
339 },
340 Value: &parser.YamlNode{
341 Lines: parser.LineRange{First: 10, Last: 10},
342 Value: "alice",
343 },
344 },
345 },
346 },
347 },
348 },
349 },
350 },
351 {
352 content: []byte("- record: foo\n expr: foo[5m] offset 10m\n"),
353 output: []parser.Rule{
354 {
355 Lines: parser.LineRange{First: 1, Last: 2},
356 RecordingRule: &parser.RecordingRule{
357 Record: parser.YamlNode{
358 Lines: parser.LineRange{First: 1, Last: 1},
359 Value: "foo",
360 },
361 Expr: parser.PromQLExpr{
362 Value: &parser.YamlNode{
363 Lines: parser.LineRange{First: 2, Last: 2},
364 Value: "foo[5m] offset 10m",
365 },
366 },
367 },
368 },
369 },
370 },
371 {
372 content: []byte(`
373- record: name
374 expr: sum(foo)
375 labels:
376 foo: bar
377 bob: alice
378`),
379 output: []parser.Rule{
380 {
381 Lines: parser.LineRange{First: 2, Last: 6},
382 RecordingRule: &parser.RecordingRule{
383 Record: parser.YamlNode{
384 Lines: parser.LineRange{First: 2, Last: 2},
385 Value: "name",
386 },
387 Expr: parser.PromQLExpr{
388 Value: &parser.YamlNode{
389 Lines: parser.LineRange{First: 3, Last: 3},
390 Value: "sum(foo)",
391 },
392 },
393 Labels: &parser.YamlMap{
394 Lines: parser.LineRange{First: 4, Last: 6},
395 Key: &parser.YamlNode{
396 Lines: parser.LineRange{First: 4, Last: 4},
397 Value: "labels",
398 },
399 Items: []*parser.YamlKeyValue{
400 {
401 Key: &parser.YamlNode{
402 Lines: parser.LineRange{First: 5, Last: 5},
403 Value: "foo",
404 },
405 Value: &parser.YamlNode{
406 Lines: parser.LineRange{First: 5, Last: 5},
407 Value: "bar",
408 },
409 },
410 {
411 Key: &parser.YamlNode{
412 Lines: parser.LineRange{First: 6, Last: 6},
413 Value: "bob",
414 },
415 Value: &parser.YamlNode{
416 Lines: parser.LineRange{First: 6, Last: 6},
417 Value: "alice",
418 },
419 },
420 },
421 },
422 },
423 },
424 },
425 },
426 {
427 content: []byte(`
428groups:
429- name: custom_rules
430 rules:
431 - record: name
432 expr: sum(foo)
433 labels:
434 foo: bar
435 bob: alice
436`),
437 output: []parser.Rule{
438 {
439 Lines: parser.LineRange{First: 5, Last: 9},
440 RecordingRule: &parser.RecordingRule{
441 Record: parser.YamlNode{
442 Lines: parser.LineRange{First: 5, Last: 5},
443 Value: "name",
444 },
445 Expr: parser.PromQLExpr{
446 Value: &parser.YamlNode{
447 Lines: parser.LineRange{First: 6, Last: 6},
448 Value: "sum(foo)",
449 },
450 },
451 Labels: &parser.YamlMap{
452 Lines: parser.LineRange{First: 7, Last: 9},
453 Key: &parser.YamlNode{
454 Lines: parser.LineRange{First: 7, Last: 7},
455 Value: "labels",
456 },
457 Items: []*parser.YamlKeyValue{
458 {
459 Key: &parser.YamlNode{
460 Lines: parser.LineRange{First: 8, Last: 8},
461 Value: "foo",
462 },
463 Value: &parser.YamlNode{
464 Lines: parser.LineRange{First: 8, Last: 8},
465 Value: "bar",
466 },
467 },
468 {
469 Key: &parser.YamlNode{
470 Lines: parser.LineRange{First: 9, Last: 9},
471 Value: "bob",
472 },
473 Value: &parser.YamlNode{
474 Lines: parser.LineRange{First: 9, Last: 9},
475 Value: "alice",
476 },
477 },
478 },
479 },
480 },
481 },
482 },
483 },
484 {
485 content: []byte(`- alert: Down
486 expr: |
487 up == 0
488 for: |+
489 11m
490 labels:
491 severity: critical
492 annotations:
493 uri: https://docs.example.com/down.html
494
495- record: foo
496 expr: |-
497 bar
498 /
499 baz > 1
500 labels: {}
501`),
502 output: []parser.Rule{
503 {
504 Lines: parser.LineRange{First: 1, Last: 9},
505 AlertingRule: &parser.AlertingRule{
506 Alert: parser.YamlNode{
507 Lines: parser.LineRange{First: 1, Last: 1},
508 Value: "Down",
509 },
510 Expr: parser.PromQLExpr{
511 Value: &parser.YamlNode{
512 Lines: parser.LineRange{First: 2, Last: 3},
513 Value: "up == 0\n",
514 },
515 },
516 For: &parser.YamlNode{
517 Lines: parser.LineRange{First: 4, Last: 5},
518 Value: "11m\n",
519 },
520 Labels: &parser.YamlMap{
521 Lines: parser.LineRange{First: 6, Last: 7},
522 Key: &parser.YamlNode{
523 Lines: parser.LineRange{First: 6, Last: 6},
524 Value: "labels",
525 },
526 Items: []*parser.YamlKeyValue{
527 {
528 Key: &parser.YamlNode{
529 Lines: parser.LineRange{First: 7, Last: 7},
530 Value: "severity",
531 },
532 Value: &parser.YamlNode{
533 Lines: parser.LineRange{First: 7, Last: 7},
534 Value: "critical",
535 },
536 },
537 },
538 },
539 Annotations: &parser.YamlMap{
540 Lines: parser.LineRange{First: 8, Last: 9},
541 Key: &parser.YamlNode{
542 Lines: parser.LineRange{First: 8, Last: 8},
543 Value: "annotations",
544 },
545 Items: []*parser.YamlKeyValue{
546 {
547 Key: &parser.YamlNode{
548 Lines: parser.LineRange{First: 9, Last: 9},
549 Value: "uri",
550 },
551 Value: &parser.YamlNode{
552 Lines: parser.LineRange{First: 9, Last: 9},
553 Value: "https://docs.example.com/down.html",
554 },
555 },
556 },
557 },
558 },
559 },
560 {
561 Lines: parser.LineRange{First: 11, Last: 16},
562 RecordingRule: &parser.RecordingRule{
563 Record: parser.YamlNode{
564 Lines: parser.LineRange{First: 11, Last: 11},
565 Value: "foo",
566 },
567 Expr: parser.PromQLExpr{
568 Value: &parser.YamlNode{
569 Lines: parser.LineRange{First: 12, Last: 15},
570 Value: "bar\n/\nbaz > 1",
571 },
572 },
573 Labels: &parser.YamlMap{
574 Lines: parser.LineRange{First: 16, Last: 16},
575 Key: &parser.YamlNode{
576 Lines: parser.LineRange{First: 16, Last: 16},
577 Value: "labels",
578 },
579 },
580 },
581 },
582 },
583 },
584 {
585 content: []byte(`- alert: Foo
586 expr:
587 (
588 xxx
589 -
590 yyy
591 ) * bar > 0
592 and on(instance, device) baz
593 for: 30m
594`),
595 output: []parser.Rule{
596 {
597 Lines: parser.LineRange{First: 1, Last: 9},
598 AlertingRule: &parser.AlertingRule{
599 Alert: parser.YamlNode{
600 Lines: parser.LineRange{First: 1, Last: 1},
601 Value: "Foo",
602 },
603 Expr: parser.PromQLExpr{
604 Value: &parser.YamlNode{
605 Lines: parser.LineRange{First: 2, Last: 8},
606 Value: "( xxx - yyy ) * bar > 0 and on(instance, device) baz",
607 },
608 },
609 For: &parser.YamlNode{
610 Lines: parser.LineRange{First: 9, Last: 9},
611 Value: "30m",
612 },
613 },
614 },
615 },
616 },
617 {
618 content: []byte(`---
619kind: ConfigMap
620apiVersion: v1
621metadata:
622 name: example-app-alerts
623 labels:
624 app: example-app
625data:
626 alerts: |
627 groups:
628 - name: example-app-alerts
629 rules:
630 - alert: Example_High_Restart_Rate
631 expr: sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )
632---
633kind: ConfigMap
634apiVersion: v1
635metadata:
636 name: other
637 labels:
638 app: other
639data:
640 alerts: |
641 groups:
642 - name: other alerts
643 rules:
644 - alert: Example_High_Restart_Rate
645 expr: "1"
646
647`),
648 output: []parser.Rule{
649 {
650 Lines: parser.LineRange{First: 13, Last: 14},
651 AlertingRule: &parser.AlertingRule{
652 Alert: parser.YamlNode{
653 Lines: parser.LineRange{First: 13, Last: 13},
654 Value: "Example_High_Restart_Rate",
655 },
656 Expr: parser.PromQLExpr{
657 Value: &parser.YamlNode{
658 Lines: parser.LineRange{First: 14, Last: 14},
659 Value: `sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )`,
660 },
661 },
662 },
663 },
664 {
665 Lines: parser.LineRange{First: 27, Last: 28},
666 AlertingRule: &parser.AlertingRule{
667 Expr: parser.PromQLExpr{
668 Value: &parser.YamlNode{Value: "1", Lines: parser.LineRange{First: 28, Last: 28}},
669 },
670 Alert: parser.YamlNode{Value: "Example_High_Restart_Rate", Lines: parser.LineRange{First: 27, Last: 27}},
671 },
672 },
673 },
674 },
675 {
676 content: []byte(`---
677kind: ConfigMap
678apiVersion: v1
679metadata:
680 name: example-app-alerts
681 labels:
682 app: example-app
683data:
684 alerts: |
685 groups:
686 - name: example-app-alerts
687 rules:
688 - alert: Example_Is_Down
689 expr: kube_deployment_status_replicas_available{namespace="example-app"} < 1
690 for: 5m
691 labels:
692 priority: "2"
693 environment: production
694 annotations:
695 summary: "No replicas for Example have been running for 5 minutes"
696
697 - alert: Example_High_Restart_Rate
698 expr: sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )
699`),
700 output: []parser.Rule{
701 {
702 Lines: parser.LineRange{First: 13, Last: 20},
703 AlertingRule: &parser.AlertingRule{
704 Alert: parser.YamlNode{
705 Lines: parser.LineRange{First: 13, Last: 13},
706 Value: "Example_Is_Down",
707 },
708 Expr: parser.PromQLExpr{
709 Value: &parser.YamlNode{
710 Lines: parser.LineRange{First: 14, Last: 14},
711 Value: `kube_deployment_status_replicas_available{namespace="example-app"} < 1`,
712 },
713 },
714 For: &parser.YamlNode{
715 Lines: parser.LineRange{First: 15, Last: 15},
716 Value: "5m",
717 },
718 Labels: &parser.YamlMap{
719 Lines: parser.LineRange{First: 16, Last: 18},
720 Key: &parser.YamlNode{
721 Lines: parser.LineRange{First: 16, Last: 16},
722 Value: "labels",
723 },
724 Items: []*parser.YamlKeyValue{
725 {
726 Key: &parser.YamlNode{
727 Lines: parser.LineRange{First: 17, Last: 17},
728 Value: "priority",
729 },
730 Value: &parser.YamlNode{
731 Lines: parser.LineRange{First: 17, Last: 17},
732 Value: "2",
733 },
734 },
735 {
736 Key: &parser.YamlNode{
737 Lines: parser.LineRange{First: 18, Last: 18},
738 Value: "environment",
739 },
740 Value: &parser.YamlNode{
741 Lines: parser.LineRange{First: 18, Last: 18},
742 Value: "production",
743 },
744 },
745 },
746 },
747 Annotations: &parser.YamlMap{
748 Lines: parser.LineRange{First: 19, Last: 20},
749 Key: &parser.YamlNode{
750 Lines: parser.LineRange{First: 19, Last: 19},
751 Value: "annotations",
752 },
753 Items: []*parser.YamlKeyValue{
754 {
755 Key: &parser.YamlNode{
756 Lines: parser.LineRange{First: 20, Last: 20},
757 Value: "summary",
758 },
759 Value: &parser.YamlNode{
760 Lines: parser.LineRange{First: 20, Last: 20},
761 Value: "No replicas for Example have been running for 5 minutes",
762 },
763 },
764 },
765 },
766 },
767 },
768 {
769 Lines: parser.LineRange{First: 22, Last: 23},
770 AlertingRule: &parser.AlertingRule{
771 Alert: parser.YamlNode{
772 Lines: parser.LineRange{First: 22, Last: 22},
773 Value: "Example_High_Restart_Rate",
774 },
775 Expr: parser.PromQLExpr{
776 Value: &parser.YamlNode{
777 Lines: parser.LineRange{First: 23, Last: 23},
778 Value: `sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )`,
779 },
780 },
781 },
782 },
783 },
784 },
785 {
786 content: []byte(`groups:
787- name: "haproxy.api_server.rules"
788 rules:
789 - alert: HaproxyServerHealthcheckFailure
790 expr: increase(haproxy_server_check_failures_total[15m]) > 100
791 for: 5m
792 labels:
793 severity: 24x7
794 annotations:
795 summary: "HAProxy server healthcheck failure (instance {{ $labels.instance }})"
796 description: "Some server healthcheck are failing on {{ $labels.server }}\n VALUE = {{ $value }}\n LABELS: {{ $labels }}"
797`),
798 output: []parser.Rule{
799 {
800 Lines: parser.LineRange{First: 4, Last: 13},
801 AlertingRule: &parser.AlertingRule{
802 Alert: parser.YamlNode{
803 Lines: parser.LineRange{First: 4, Last: 4},
804 Value: "HaproxyServerHealthcheckFailure",
805 },
806 Expr: parser.PromQLExpr{
807 Value: &parser.YamlNode{
808 Lines: parser.LineRange{First: 5, Last: 5},
809 Value: "increase(haproxy_server_check_failures_total[15m]) > 100",
810 },
811 },
812 For: &parser.YamlNode{
813 Lines: parser.LineRange{First: 6, Last: 6},
814 Value: "5m",
815 },
816 Labels: &parser.YamlMap{
817 Lines: parser.LineRange{First: 7, Last: 8},
818 Key: &parser.YamlNode{
819 Lines: parser.LineRange{First: 7, Last: 7},
820 Value: "labels",
821 },
822 Items: []*parser.YamlKeyValue{
823 {
824 Key: &parser.YamlNode{
825 Lines: parser.LineRange{First: 8, Last: 8},
826 Value: "severity",
827 },
828 Value: &parser.YamlNode{
829 Lines: parser.LineRange{First: 8, Last: 8},
830 Value: "24x7",
831 },
832 },
833 },
834 },
835 Annotations: &parser.YamlMap{
836 Lines: parser.LineRange{First: 9, Last: 13},
837 Key: &parser.YamlNode{
838 Lines: parser.LineRange{First: 9, Last: 9},
839 Value: "annotations",
840 },
841 Items: []*parser.YamlKeyValue{
842 {
843 Key: &parser.YamlNode{
844 Lines: parser.LineRange{First: 10, Last: 10},
845 Value: "summary",
846 },
847 Value: &parser.YamlNode{
848 Lines: parser.LineRange{First: 10, Last: 10},
849 Value: "HAProxy server healthcheck failure (instance {{ $labels.instance }})",
850 },
851 },
852 {
853 Key: &parser.YamlNode{
854 Lines: parser.LineRange{First: 11, Last: 11},
855 Value: "description",
856 },
857 Value: &parser.YamlNode{
858 // FIXME https://github.com/cloudflare/pint/issues/20
859 // Should be Lines: [11]
860 Lines: parser.LineRange{First: 11, Last: 13},
861 // Should be `Some ...` since \n should be escaped
862 Value: "Some server healthcheck are failing on {{ $labels.server }}\n VALUE = {{ $value }}\n LABELS: {{ $labels }}",
863 },
864 },
865 },
866 },
867 },
868 },
869 },
870 },
871 {
872 content: []byte(`groups:
873- name: certmanager
874 rules:
875 # pint disable before recordAnchor
876 - &recordAnchor # pint disable recordAnchor
877 record: name1 # pint disable name1
878 expr: expr1 # pint disable expr1
879 # pint disable after expr1
880 - <<: *recordAnchor
881 expr: expr2
882 - <<: *recordAnchor
883`),
884 output: []parser.Rule{
885 {
886 Lines: parser.LineRange{First: 6, Last: 7},
887 Comments: []comments.Comment{
888 {
889 Type: comments.DisableType,
890 Value: comments.Disable{Match: "before recordAnchor"},
891 },
892 {
893 Type: comments.DisableType,
894 Value: comments.Disable{Match: "recordAnchor"},
895 },
896 {
897 Type: comments.DisableType,
898 Value: comments.Disable{Match: "name1"},
899 },
900 {
901 Type: comments.DisableType,
902 Value: comments.Disable{Match: "after expr1"},
903 },
904 {
905 Type: comments.DisableType,
906 Value: comments.Disable{Match: "expr1"},
907 },
908 },
909 RecordingRule: &parser.RecordingRule{
910 Record: parser.YamlNode{
911 Lines: parser.LineRange{First: 6, Last: 6},
912 Value: "name1",
913 },
914 Expr: parser.PromQLExpr{
915 Value: &parser.YamlNode{
916 Lines: parser.LineRange{First: 7, Last: 7},
917 Value: "expr1",
918 },
919 },
920 },
921 },
922 {
923 Comments: []comments.Comment{
924 {
925 Type: comments.DisableType,
926 Value: comments.Disable{Match: "before recordAnchor"},
927 },
928 {
929 Type: comments.DisableType,
930 Value: comments.Disable{Match: "recordAnchor"},
931 },
932 {
933 Type: comments.DisableType,
934 Value: comments.Disable{Match: "name1"},
935 },
936 },
937 Lines: parser.LineRange{First: 6, Last: 10},
938 RecordingRule: &parser.RecordingRule{
939 Record: parser.YamlNode{
940 Lines: parser.LineRange{First: 6, Last: 6},
941 Value: "name1",
942 },
943 Expr: parser.PromQLExpr{
944 Value: &parser.YamlNode{
945 Lines: parser.LineRange{First: 10, Last: 10},
946 Value: "expr2",
947 },
948 },
949 },
950 },
951 {
952 Comments: []comments.Comment{
953 {
954 Type: comments.DisableType,
955 Value: comments.Disable{Match: "before recordAnchor"},
956 },
957 {
958 Type: comments.DisableType,
959 Value: comments.Disable{Match: "recordAnchor"},
960 },
961 {
962 Type: comments.DisableType,
963 Value: comments.Disable{Match: "name1"},
964 },
965 {
966 Type: comments.DisableType,
967 Value: comments.Disable{Match: "after expr1"},
968 },
969 {
970 Type: comments.DisableType,
971 Value: comments.Disable{Match: "expr1"},
972 },
973 },
974 Lines: parser.LineRange{First: 6, Last: 7},
975 RecordingRule: &parser.RecordingRule{
976 Record: parser.YamlNode{
977 Lines: parser.LineRange{First: 6, Last: 6},
978 Value: "name1",
979 },
980 Expr: parser.PromQLExpr{
981 Value: &parser.YamlNode{
982 Lines: parser.LineRange{First: 7, Last: 7},
983 Value: "expr1",
984 },
985 },
986 },
987 },
988 },
989 },
990 {
991 content: []byte(`groups:
992- name: certmanager
993 rules:
994 - record: name1
995 expr: expr1
996 labels: &labelsAnchor
997 label1: val1
998 label2: val2
999 - record: name2
1000 expr: expr2
1001 labels: *labelsAnchor
1002 # pint disable foot comment
1003`),
1004 output: []parser.Rule{
1005 {
1006 Lines: parser.LineRange{First: 4, Last: 8},
1007 RecordingRule: &parser.RecordingRule{
1008 Record: parser.YamlNode{
1009 Lines: parser.LineRange{First: 4, Last: 4},
1010 Value: "name1",
1011 },
1012 Expr: parser.PromQLExpr{
1013 Value: &parser.YamlNode{
1014 Lines: parser.LineRange{First: 5, Last: 5},
1015 Value: "expr1",
1016 },
1017 },
1018 Labels: &parser.YamlMap{
1019 Lines: parser.LineRange{First: 6, Last: 8},
1020 Key: &parser.YamlNode{
1021 Lines: parser.LineRange{First: 6, Last: 6},
1022 Value: "labels",
1023 },
1024 Items: []*parser.YamlKeyValue{
1025 {
1026 Key: &parser.YamlNode{
1027 Lines: parser.LineRange{First: 7, Last: 7},
1028 Value: "label1",
1029 },
1030 Value: &parser.YamlNode{
1031 Lines: parser.LineRange{First: 7, Last: 7},
1032 Value: "val1",
1033 },
1034 },
1035 {
1036 Key: &parser.YamlNode{
1037 Lines: parser.LineRange{First: 8, Last: 8},
1038 Value: "label2",
1039 },
1040 Value: &parser.YamlNode{
1041 Lines: parser.LineRange{First: 8, Last: 8},
1042 Value: "val2",
1043 },
1044 },
1045 },
1046 },
1047 },
1048 },
1049 {
1050 Comments: []comments.Comment{
1051 {
1052 Type: comments.DisableType,
1053 Value: comments.Disable{Match: "foot comment"},
1054 },
1055 },
1056 Lines: parser.LineRange{First: 9, Last: 11},
1057 RecordingRule: &parser.RecordingRule{
1058 Record: parser.YamlNode{
1059 Lines: parser.LineRange{First: 9, Last: 9},
1060 Value: "name2",
1061 },
1062 Expr: parser.PromQLExpr{
1063 Value: &parser.YamlNode{
1064 Lines: parser.LineRange{First: 10, Last: 10},
1065 Value: "expr2",
1066 },
1067 },
1068 Labels: &parser.YamlMap{
1069 Lines: parser.LineRange{First: 11, Last: 11},
1070 Key: &parser.YamlNode{
1071 Lines: parser.LineRange{First: 11, Last: 11},
1072 Value: "labels",
1073 },
1074 Items: []*parser.YamlKeyValue{
1075 {
1076 Key: &parser.YamlNode{
1077 Lines: parser.LineRange{First: 7, Last: 7},
1078 Value: "label1",
1079 },
1080 Value: &parser.YamlNode{
1081 Lines: parser.LineRange{First: 7, Last: 7},
1082 Value: "val1",
1083 },
1084 },
1085 {
1086 Key: &parser.YamlNode{
1087 Lines: parser.LineRange{First: 8, Last: 8},
1088 Value: "label2",
1089 },
1090 Value: &parser.YamlNode{
1091 Lines: parser.LineRange{First: 8, Last: 8},
1092 Value: "val2",
1093 },
1094 },
1095 },
1096 },
1097 },
1098 },
1099 },
1100 },
1101 {
1102 content: []byte("- alert:\n expr: vector(1)\n"),
1103 output: []parser.Rule{
1104 {
1105 Lines: parser.LineRange{First: 1, Last: 2},
1106 Error: parser.ParseError{Err: fmt.Errorf("alert value cannot be empty"), Line: 1},
1107 },
1108 },
1109 },
1110 {
1111 content: []byte("- alert: foo\n expr:\n"),
1112 output: []parser.Rule{
1113 {
1114 Lines: parser.LineRange{First: 1, Last: 2},
1115 Error: parser.ParseError{Err: fmt.Errorf("expr value cannot be empty"), Line: 2},
1116 },
1117 },
1118 },
1119 {
1120 content: []byte("- alert: foo\n"),
1121 output: []parser.Rule{
1122 {
1123 Lines: parser.LineRange{First: 1, Last: 1},
1124 Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 1},
1125 },
1126 },
1127 },
1128 {
1129 content: []byte("- record:\n expr:\n"),
1130 output: []parser.Rule{
1131 {
1132 Lines: parser.LineRange{First: 1, Last: 2},
1133 Error: parser.ParseError{Err: fmt.Errorf("record value cannot be empty"), Line: 1},
1134 },
1135 },
1136 },
1137 {
1138 content: []byte("- record: foo\n expr:\n"),
1139 output: []parser.Rule{
1140 {
1141 Lines: parser.LineRange{First: 1, Last: 2},
1142 Error: parser.ParseError{Err: fmt.Errorf("expr value cannot be empty"), Line: 2},
1143 },
1144 },
1145 },
1146 {
1147 content: []byte("- record: foo\n"),
1148 output: []parser.Rule{
1149 {
1150 Lines: parser.LineRange{First: 1, Last: 1},
1151 Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 1},
1152 },
1153 },
1154 },
1155 {
1156 content: []byte(string(`
1157# pint file/owner bob
1158# pint ignore/begin
1159# pint ignore/end
1160# pint disable up
1161
1162- record: foo
1163 expr: up
1164
1165# pint file/owner alice
1166
1167- record: foo
1168 expr: up
1169
1170# pint ignore/next-line
1171`)),
1172 output: []parser.Rule{
1173 {
1174 Lines: parser.LineRange{First: 7, Last: 8},
1175 RecordingRule: &parser.RecordingRule{
1176 Record: parser.YamlNode{
1177 Lines: parser.LineRange{First: 7, Last: 7},
1178 Value: "foo",
1179 },
1180 Expr: parser.PromQLExpr{
1181 Value: &parser.YamlNode{
1182 Lines: parser.LineRange{First: 8, Last: 8},
1183 Value: "up",
1184 },
1185 },
1186 },
1187 },
1188 {
1189 Lines: parser.LineRange{First: 12, Last: 13},
1190 RecordingRule: &parser.RecordingRule{
1191 Record: parser.YamlNode{
1192 Lines: parser.LineRange{First: 12, Last: 12},
1193 Value: "foo",
1194 },
1195 Expr: parser.PromQLExpr{
1196 Value: &parser.YamlNode{
1197 Lines: parser.LineRange{First: 13, Last: 13},
1198 Value: "up",
1199 },
1200 },
1201 },
1202 },
1203 },
1204 },
1205 {
1206 content: []byte(string(`
1207- alert: Template
1208 expr: &expr up == 0
1209 labels:
1210 notify: &maybe_escalate_notify chat-alerts
1211- alert: Service Down
1212 expr: *expr
1213 labels:
1214 notify: *maybe_escalate_notify
1215 summary: foo
1216`)),
1217 output: []parser.Rule{
1218 {
1219 Lines: parser.LineRange{First: 2, Last: 5},
1220 AlertingRule: &parser.AlertingRule{
1221 Alert: parser.YamlNode{
1222 Lines: parser.LineRange{First: 2, Last: 2},
1223 Value: "Template",
1224 },
1225 Expr: parser.PromQLExpr{
1226 Value: &parser.YamlNode{
1227 Lines: parser.LineRange{First: 3, Last: 3},
1228 Value: "up == 0",
1229 },
1230 },
1231 Labels: &parser.YamlMap{
1232 Lines: parser.LineRange{First: 4, Last: 5},
1233 Key: &parser.YamlNode{
1234 Lines: parser.LineRange{First: 4, Last: 4},
1235 Value: "labels",
1236 },
1237 Items: []*parser.YamlKeyValue{
1238 {
1239 Key: &parser.YamlNode{
1240 Lines: parser.LineRange{First: 5, Last: 5},
1241 Value: "notify",
1242 },
1243 Value: &parser.YamlNode{
1244 Lines: parser.LineRange{First: 5, Last: 5},
1245 Value: "chat-alerts",
1246 },
1247 },
1248 },
1249 },
1250 },
1251 },
1252 {
1253 Lines: parser.LineRange{First: 6, Last: 10},
1254 AlertingRule: &parser.AlertingRule{
1255 Alert: parser.YamlNode{
1256 Lines: parser.LineRange{First: 6, Last: 6},
1257 Value: "Service Down",
1258 },
1259 Expr: parser.PromQLExpr{
1260 Value: &parser.YamlNode{
1261 Lines: parser.LineRange{First: 7, Last: 7},
1262 Value: "up == 0",
1263 },
1264 },
1265 Labels: &parser.YamlMap{
1266 Lines: parser.LineRange{First: 8, Last: 10},
1267 Key: &parser.YamlNode{
1268 Lines: parser.LineRange{First: 8, Last: 8},
1269 Value: "labels",
1270 },
1271 Items: []*parser.YamlKeyValue{
1272 {
1273 Key: &parser.YamlNode{
1274 Lines: parser.LineRange{First: 9, Last: 9},
1275 Value: "notify",
1276 },
1277 Value: &parser.YamlNode{
1278 Lines: parser.LineRange{First: 9, Last: 9},
1279 Value: "chat-alerts",
1280 },
1281 },
1282 {
1283 Key: &parser.YamlNode{
1284 Lines: parser.LineRange{First: 10, Last: 10},
1285 Value: "summary",
1286 },
1287 Value: &parser.YamlNode{
1288 Lines: parser.LineRange{First: 10, Last: 10},
1289 Value: "foo",
1290 },
1291 },
1292 },
1293 },
1294 },
1295 },
1296 },
1297 },
1298 {
1299 content: []byte(`
1300- record: invalid metric name
1301 expr: bar
1302`),
1303 output: []parser.Rule{
1304 {
1305 Lines: parser.LineRange{First: 2, Last: 3},
1306 Error: parser.ParseError{Err: fmt.Errorf("invalid recording rule name: invalid metric name"), Line: 2},
1307 },
1308 },
1309 },
1310 {
1311 content: []byte(`
1312- record: foo
1313 expr: bar
1314 labels:
1315 "foo bar": yes
1316`),
1317 output: []parser.Rule{
1318 {
1319 Lines: parser.LineRange{First: 2, Last: 5},
1320 Error: parser.ParseError{Err: fmt.Errorf("invalid label name: foo bar"), Line: 5},
1321 },
1322 },
1323 },
1324 {
1325 content: []byte(`
1326- alert: foo
1327 expr: bar
1328 labels:
1329 "foo bar": yes
1330`),
1331 output: []parser.Rule{
1332 {
1333 Lines: parser.LineRange{First: 2, Last: 5},
1334 Error: parser.ParseError{Err: fmt.Errorf("invalid label name: foo bar"), Line: 5},
1335 },
1336 },
1337 },
1338 {
1339 content: []byte(`
1340- alert: foo
1341 expr: bar
1342 labels:
1343 "{{ $value }}": yes
1344`),
1345 output: []parser.Rule{
1346 {
1347 Lines: parser.LineRange{First: 2, Last: 5},
1348 Error: parser.ParseError{Err: fmt.Errorf("invalid label name: {{ $value }}"), Line: 5},
1349 },
1350 },
1351 },
1352 {
1353 content: []byte(`
1354- alert: foo
1355 expr: bar
1356 annotations:
1357 "foo bar": yes
1358`),
1359 output: []parser.Rule{
1360 {
1361 Lines: parser.LineRange{First: 2, Last: 5},
1362 Error: parser.ParseError{Err: fmt.Errorf("invalid annotation name: foo bar"), Line: 5},
1363 },
1364 },
1365 },
1366 {
1367 content: []byte(`
1368- alert: foo
1369 expr: bar
1370 labels:
1371 foo: ` + string("\xed\xbf\xbf")),
1372 // Label values are invalid only if they aren't valid UTF-8 strings
1373 // which also makes them unparsable by YAML.
1374 err: "yaml: invalid Unicode character",
1375 },
1376 {
1377 content: []byte(`
1378- alert: foo
1379 expr: bar
1380 annotations:
1381 "{{ $value }}": yes
1382`),
1383 output: []parser.Rule{
1384 {
1385 Lines: parser.LineRange{First: 2, Last: 5},
1386 Error: parser.ParseError{Err: fmt.Errorf("invalid annotation name: {{ $value }}"), Line: 5},
1387 },
1388 },
1389 },
1390 {
1391 content: []byte(`
1392- record: foo
1393 expr: bar
1394 keep_firing_for: 5m
1395`),
1396 output: []parser.Rule{
1397 {
1398 Lines: parser.LineRange{First: 2, Last: 4},
1399 Error: parser.ParseError{Err: fmt.Errorf("invalid field 'keep_firing_for' in recording rule"), Line: 4},
1400 },
1401 },
1402 },
1403 {
1404 content: []byte(`
1405- record: foo
1406 expr: bar
1407 for: 5m
1408`),
1409 output: []parser.Rule{
1410 {
1411 Lines: parser.LineRange{First: 2, Last: 4},
1412 Error: parser.ParseError{Err: fmt.Errorf("invalid field 'for' in recording rule"), Line: 4},
1413 },
1414 },
1415 },
1416 {
1417 content: []byte(`
1418- record: foo
1419 expr: bar
1420 annotations:
1421 foo: bar
1422`),
1423 output: []parser.Rule{
1424 {
1425 Lines: parser.LineRange{First: 2, Last: 5},
1426 Error: parser.ParseError{Err: fmt.Errorf("invalid field 'annotations' in recording rule"), Line: 4},
1427 },
1428 },
1429 },
1430 // Tag tests
1431 {
1432 content: []byte(`
1433- record: 5
1434 expr: bar
1435`),
1436 output: []parser.Rule{
1437 {
1438 Lines: parser.LineRange{First: 2, Last: 3},
1439 Error: parser.ParseError{Err: fmt.Errorf("record value must be a YAML string, got integer instead"), Line: 2},
1440 },
1441 },
1442 },
1443 {
1444 content: []byte(`
1445- alert: 5
1446 expr: bar
1447`),
1448 output: []parser.Rule{
1449 {
1450 Lines: parser.LineRange{First: 2, Last: 3},
1451 Error: parser.ParseError{Err: fmt.Errorf("alert value must be a YAML string, got integer instead"), Line: 2},
1452 },
1453 },
1454 },
1455 {
1456 content: []byte(`
1457- record: foo
1458 expr: 5
1459`),
1460 output: []parser.Rule{
1461 {
1462 Lines: parser.LineRange{First: 2, Last: 3},
1463 Error: parser.ParseError{Err: fmt.Errorf("expr value must be a YAML string, got integer instead"), Line: 3},
1464 },
1465 },
1466 },
1467 {
1468 content: []byte(`
1469- alert: foo
1470 expr: bar
1471 for: 5
1472`),
1473 output: []parser.Rule{
1474 {
1475 Lines: parser.LineRange{First: 2, Last: 4},
1476 Error: parser.ParseError{Err: fmt.Errorf("for value must be a YAML string, got integer instead"), Line: 4},
1477 },
1478 },
1479 },
1480 {
1481 content: []byte(`
1482- alert: foo
1483 expr: bar
1484 keep_firing_for: 5
1485`),
1486 output: []parser.Rule{
1487 {
1488 Lines: parser.LineRange{First: 2, Last: 4},
1489 Error: parser.ParseError{Err: fmt.Errorf("keep_firing_for value must be a YAML string, got integer instead"), Line: 4},
1490 },
1491 },
1492 },
1493 {
1494 content: []byte(`
1495- record: foo
1496 expr: bar
1497 labels: []
1498`),
1499 output: []parser.Rule{
1500 {
1501 Lines: parser.LineRange{First: 2, Last: 4},
1502 Error: parser.ParseError{Err: fmt.Errorf("labels value must be a YAML mapping, got list instead"), Line: 4},
1503 },
1504 },
1505 },
1506 {
1507 content: []byte(`
1508- alert: foo
1509 expr: bar
1510 labels: {}
1511 annotations: []
1512`),
1513 output: []parser.Rule{
1514 {
1515 Lines: parser.LineRange{First: 2, Last: 5},
1516 Error: parser.ParseError{Err: fmt.Errorf("annotations value must be a YAML mapping, got list instead"), Line: 5},
1517 },
1518 },
1519 },
1520 {
1521 content: []byte(`
1522- alert: foo
1523 expr: bar
1524 labels:
1525 foo: 3
1526 annotations:
1527 bar: "5"
1528`),
1529 output: []parser.Rule{
1530 {
1531 Lines: parser.LineRange{First: 2, Last: 7},
1532 Error: parser.ParseError{Err: fmt.Errorf("labels foo value must be a YAML string, got integer instead"), Line: 5},
1533 },
1534 },
1535 },
1536 {
1537 content: []byte(`
1538- alert: foo
1539 expr: bar
1540 labels: {}
1541 annotations:
1542 foo: "3"
1543 bar: 5
1544`),
1545 output: []parser.Rule{
1546 {
1547 Lines: parser.LineRange{First: 2, Last: 7},
1548 Error: parser.ParseError{Err: fmt.Errorf("annotations bar value must be a YAML string, got integer instead"), Line: 7},
1549 },
1550 },
1551 },
1552 {
1553 content: []byte(`
1554- record: foo
1555 expr: bar
1556 labels: 4
1557`),
1558 output: []parser.Rule{
1559 {
1560 Lines: parser.LineRange{First: 2, Last: 4},
1561 Error: parser.ParseError{Err: fmt.Errorf("labels value must be a YAML mapping, got integer instead"), Line: 4},
1562 },
1563 },
1564 },
1565 {
1566 content: []byte(`
1567- record: foo
1568 expr: bar
1569 labels: true
1570`),
1571 output: []parser.Rule{
1572 {
1573 Lines: parser.LineRange{First: 2, Last: 4},
1574 Error: parser.ParseError{Err: fmt.Errorf("labels value must be a YAML mapping, got bool instead"), Line: 4},
1575 },
1576 },
1577 },
1578 {
1579 content: []byte(`
1580- record: foo
1581 expr: bar
1582 labels: null
1583`),
1584 output: []parser.Rule{
1585 {
1586 Lines: parser.LineRange{First: 2, Last: 4},
1587 RecordingRule: &parser.RecordingRule{
1588 Record: parser.YamlNode{
1589 Lines: parser.LineRange{First: 2, Last: 2},
1590 Value: "foo",
1591 },
1592 Expr: parser.PromQLExpr{
1593 Value: &parser.YamlNode{
1594 Lines: parser.LineRange{First: 3, Last: 3},
1595 Value: "bar",
1596 },
1597 },
1598 Labels: &parser.YamlMap{
1599 Lines: parser.LineRange{First: 4, Last: 4},
1600 Key: &parser.YamlNode{
1601 Lines: parser.LineRange{First: 4, Last: 4},
1602 Value: "labels",
1603 },
1604 },
1605 },
1606 },
1607 },
1608 },
1609 {
1610 content: []byte(`
1611- record: true
1612 expr: bar
1613`),
1614 output: []parser.Rule{
1615 {
1616 Lines: parser.LineRange{First: 2, Last: 3},
1617 Error: parser.ParseError{Err: fmt.Errorf("record value must be a YAML string, got bool instead"), Line: 2},
1618 },
1619 },
1620 },
1621 {
1622 content: []byte(`
1623- record:
1624 query: foo
1625 expr: bar
1626`),
1627 output: []parser.Rule{
1628 {
1629 Lines: parser.LineRange{First: 2, Last: 4},
1630 Error: parser.ParseError{Err: fmt.Errorf("record value must be a YAML string, got mapping instead"), Line: 3},
1631 },
1632 },
1633 },
1634 {
1635 content: []byte(`
1636- record: foo
1637 expr: bar
1638 labels: some
1639`),
1640 output: []parser.Rule{
1641 {
1642 Lines: parser.LineRange{First: 2, Last: 4},
1643 Error: parser.ParseError{Err: fmt.Errorf("labels value must be a YAML mapping, got string instead"), Line: 4},
1644 },
1645 },
1646 },
1647 {
1648 content: []byte(`
1649- record: foo
1650 expr: bar
1651 labels: !!binary "SGVsbG8sIFdvcmxkIQ=="
1652`),
1653 output: []parser.Rule{
1654 {
1655 Lines: parser.LineRange{First: 2, Last: 4},
1656 Error: parser.ParseError{Err: fmt.Errorf("labels value must be a YAML mapping, got binary data instead"), Line: 4},
1657 },
1658 },
1659 },
1660 {
1661 content: []byte(`
1662- alert: foo
1663 expr: bar
1664 for: 1.23
1665`),
1666 output: []parser.Rule{
1667 {
1668 Lines: parser.LineRange{First: 2, Last: 4},
1669 Error: parser.ParseError{Err: fmt.Errorf("for value must be a YAML string, got float instead"), Line: 4},
1670 },
1671 },
1672 },
1673 {
1674 content: []byte(`
1675- record: foo
1676 expr: bar
1677 labels: !!garbage "SGVsbG8sIFdvcmxkIQ=="
1678`),
1679 output: []parser.Rule{
1680 {
1681 Lines: parser.LineRange{First: 2, Last: 4},
1682 Error: parser.ParseError{Err: fmt.Errorf("labels value must be a YAML mapping, got garbage instead"), Line: 4},
1683 },
1684 },
1685 },
1686 {
1687 content: []byte(`
1688- record: foo
1689 expr: bar
1690 labels: !! "SGVsbG8sIFdvcmxkIQ=="
1691`),
1692 err: "yaml: line 4: did not find expected tag URI",
1693 },
1694 {
1695 content: []byte(`
1696- record: &foo foo
1697 expr: bar
1698 labels: *foo
1699`),
1700 output: []parser.Rule{
1701 {
1702 Lines: parser.LineRange{First: 2, Last: 4},
1703 Error: parser.ParseError{Err: fmt.Errorf("labels value must be a YAML mapping, got string instead"), Line: 4},
1704 },
1705 },
1706 },
1707 // Multi-document tests
1708 {
1709 content: []byte(`---
1710- expr: foo
1711 record: foo
1712---
1713- expr: bar
1714`),
1715 output: []parser.Rule{
1716 {
1717 Lines: parser.LineRange{First: 2, Last: 3},
1718 RecordingRule: &parser.RecordingRule{
1719 Record: parser.YamlNode{
1720 Lines: parser.LineRange{First: 3, Last: 3},
1721 Value: "foo",
1722 },
1723 Expr: parser.PromQLExpr{
1724 Value: &parser.YamlNode{
1725 Lines: parser.LineRange{First: 2, Last: 2},
1726 Value: "foo",
1727 },
1728 },
1729 },
1730 },
1731 {
1732 Lines: parser.LineRange{First: 5, Last: 5},
1733 Error: parser.ParseError{Err: fmt.Errorf("incomplete rule, no alert or record key"), Line: 5},
1734 },
1735 },
1736 },
1737 {
1738 content: []byte(`---
1739- expr: foo
1740 record: foo
1741---
1742- expr: bar
1743 record: bar
1744 expr: bar
1745`),
1746 output: []parser.Rule{
1747 {
1748 Lines: parser.LineRange{First: 2, Last: 3},
1749 RecordingRule: &parser.RecordingRule{
1750 Record: parser.YamlNode{
1751 Lines: parser.LineRange{First: 3, Last: 3},
1752 Value: "foo",
1753 },
1754 Expr: parser.PromQLExpr{
1755 Value: &parser.YamlNode{
1756 Lines: parser.LineRange{First: 2, Last: 2},
1757 Value: "foo",
1758 },
1759 },
1760 },
1761 },
1762 {
1763 Lines: parser.LineRange{First: 5, Last: 7},
1764 Error: parser.ParseError{Err: fmt.Errorf("duplicated expr key"), Line: 7},
1765 },
1766 },
1767 },
1768 {
1769 content: []byte(`---
1770- expr: foo
1771 record: foo
1772---
1773- expr: bar
1774 alert: foo
1775`),
1776 output: []parser.Rule{
1777 {
1778 Lines: parser.LineRange{First: 2, Last: 3},
1779 RecordingRule: &parser.RecordingRule{
1780 Record: parser.YamlNode{
1781 Lines: parser.LineRange{First: 3, Last: 3},
1782 Value: "foo",
1783 },
1784 Expr: parser.PromQLExpr{
1785 Value: &parser.YamlNode{
1786 Lines: parser.LineRange{First: 2, Last: 2},
1787 Value: "foo",
1788 },
1789 },
1790 },
1791 },
1792 {
1793 Lines: parser.LineRange{First: 5, Last: 6},
1794 AlertingRule: &parser.AlertingRule{
1795 Alert: parser.YamlNode{
1796 Lines: parser.LineRange{First: 6, Last: 6},
1797 Value: "foo",
1798 },
1799 Expr: parser.PromQLExpr{
1800 Value: &parser.YamlNode{
1801 Lines: parser.LineRange{First: 5, Last: 5},
1802 Value: "bar",
1803 },
1804 },
1805 },
1806 },
1807 },
1808 },
1809 }
1810
1811 alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true })
1812 ignorePrometheusExpr := cmp.FilterValues(func(x, y interface{}) bool {
1813 _, xe := x.(*parser.PromQLNode)
1814 _, ye := y.(*parser.PromQLNode)
1815 return xe || ye
1816 }, alwaysEqual)
1817
1818 cmpErrorText := cmp.Comparer(func(x, y interface{}) bool {
1819 xe := x.(error)
1820 ye := y.(error)
1821 return xe.Error() == ye.Error()
1822 })
1823 sameErrorText := cmp.FilterValues(func(x, y interface{}) bool {
1824 _, xe := x.(error)
1825 _, ye := y.(error)
1826 return xe && ye
1827 }, cmpErrorText)
1828
1829 for i, tc := range testCases {
1830 t.Run(strconv.Itoa(i+1), func(t *testing.T) {
1831 t.Logf("--- Content ---%s--- END ---", tc.content)
1832
1833 p := parser.NewParser()
1834 output, err := p.Parse(tc.content)
1835
1836 if tc.err != "" {
1837 require.EqualError(t, err, tc.err)
1838 } else {
1839 require.NoError(t, err)
1840 }
1841
1842 if diff := cmp.Diff(tc.output, output, ignorePrometheusExpr, sameErrorText); diff != "" {
1843 t.Errorf("Parse() returned wrong output (-want +got):\n%s", diff)
1844 return
1845 }
1846 })
1847 }
1848}
1849