cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e622a47c6fc350caabb3fe75c25cc3bfbd6feab6

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/parser_test.go

7799lines · modecode

1package parser_test
2
3import (
4 "bufio"
5 "bytes"
6 "errors"
7 "os"
8 "strconv"
9 "testing"
10 "time"
11
12 "github.com/prometheus/common/model"
13 "github.com/stretchr/testify/require"
14
15 "github.com/cloudflare/pint/internal/comments"
16 "github.com/cloudflare/pint/internal/diags"
17 "github.com/cloudflare/pint/internal/parser"
18
19 "github.com/google/go-cmp/cmp"
20 "github.com/google/go-cmp/cmp/cmpopts"
21)
22
23func TestParse(t *testing.T) {
24 type testCaseT struct {
25 input []byte
26 output parser.File
27 strict bool
28 schema parser.Schema
29 names model.ValidationScheme
30 }
31
32 testCases := []testCaseT{
33 {
34 input: nil,
35 output: parser.File{
36 IsRelaxed: true,
37 },
38 },
39 {
40 input: []byte{},
41 output: parser.File{
42 IsRelaxed: true,
43 },
44 },
45 {
46 input: []byte(""),
47 output: parser.File{
48 IsRelaxed: true,
49 },
50 },
51 {
52 input: []byte("\n"),
53 output: parser.File{
54 IsRelaxed: true,
55 },
56 },
57 {
58 input: []byte("\n\n\n"),
59 output: parser.File{
60 IsRelaxed: true,
61 },
62 },
63 {
64 input: []byte("---"),
65 output: parser.File{
66 IsRelaxed: true,
67 },
68 },
69 {
70 input: []byte("---\n"),
71 output: parser.File{
72 IsRelaxed: true,
73 },
74 },
75 {
76 input: []byte("\n---\n\n---\n"),
77 output: parser.File{
78 IsRelaxed: true,
79 },
80 },
81 {
82 input: []byte("\n---\n\n---\n---"),
83 output: parser.File{
84 IsRelaxed: true,
85 },
86 },
87 {
88 input: []byte(string("! !00 \xf6")),
89 output: parser.File{
90 IsRelaxed: true,
91 Error: parser.ParseError{
92 Err: errors.New("yaml: incomplete UTF-8 octet sequence"),
93 Line: 1,
94 },
95 },
96 },
97 {
98 input: []byte("- 0: 0\n 00000000: 000000\n 000000:00000000000: 00000000\n 00000000000:000000: 0000000000000000000000000000000000\n 000000: 0000000\n expr: |"),
99 output: parser.File{
100 IsRelaxed: true,
101 Groups: []parser.Group{
102 {
103 Rules: []parser.Rule{
104 {
105 Lines: diags.LineRange{First: 1, Last: 6},
106 Error: parser.ParseError{Err: errors.New("incomplete rule, no alert or record key"), Line: 6},
107 },
108 },
109 },
110 },
111 },
112 },
113 {
114 input: []byte("- record: |\n multiline\n"),
115 output: parser.File{
116 IsRelaxed: true,
117 Groups: []parser.Group{
118 {
119 Rules: []parser.Rule{
120 {
121 Lines: diags.LineRange{First: 1, Last: 2},
122 Error: parser.ParseError{Err: errors.New("missing expr key"), Line: 2},
123 },
124 },
125 },
126 },
127 },
128 },
129 {
130 input: []byte(`---
131- expr: foo
132 record: foo
133---
134- expr: bar
135`),
136 output: parser.File{
137 IsRelaxed: true,
138 Groups: []parser.Group{
139 {
140 Rules: []parser.Rule{
141 {
142 RecordingRule: &parser.RecordingRule{
143 Expr: parser.PromQLExpr{
144 Value: &parser.YamlNode{
145 Value: "foo",
146 Pos: diags.PositionRanges{
147 {Line: 2, FirstColumn: 9, LastColumn: 11},
148 },
149 },
150 },
151 Record: parser.YamlNode{
152 Value: "foo",
153 Pos: diags.PositionRanges{
154 {Line: 3, FirstColumn: 11, LastColumn: 13},
155 },
156 },
157 },
158 Lines: diags.LineRange{First: 2, Last: 3},
159 },
160 },
161 },
162 {
163 Rules: []parser.Rule{
164 {
165 Lines: diags.LineRange{First: 5, Last: 5},
166 Error: parser.ParseError{Err: errors.New("incomplete rule, no alert or record key"), Line: 5},
167 },
168 },
169 },
170 },
171 },
172 },
173 {
174 input: []byte("- expr: foo\n"),
175 output: parser.File{
176 IsRelaxed: true,
177 Groups: []parser.Group{
178 {
179 Rules: []parser.Rule{
180 {
181 Lines: diags.LineRange{First: 1, Last: 1},
182 Error: parser.ParseError{Err: errors.New("incomplete rule, no alert or record key"), Line: 1},
183 },
184 },
185 },
186 },
187 },
188 },
189 {
190 input: []byte("- alert: foo\n"),
191 output: parser.File{
192 IsRelaxed: true,
193 Groups: []parser.Group{
194 {
195 Rules: []parser.Rule{
196 {
197 Lines: diags.LineRange{First: 1, Last: 1},
198 Error: parser.ParseError{Err: errors.New("missing expr key"), Line: 1},
199 },
200 },
201 },
202 },
203 },
204 },
205 {
206 input: []byte("- alert: foo\n record: foo\n"),
207 output: parser.File{
208 IsRelaxed: true,
209 Groups: []parser.Group{
210 {
211 Rules: []parser.Rule{
212 {
213 Lines: diags.LineRange{First: 1, Last: 2},
214 Error: parser.ParseError{Err: errors.New("got both record and alert keys in a single rule"), Line: 1},
215 },
216 },
217 },
218 },
219 },
220 },
221 {
222 input: []byte("- record: foo\n labels:\n foo: bar\n"),
223 output: parser.File{
224 IsRelaxed: true,
225 Groups: []parser.Group{
226 {
227 Rules: []parser.Rule{
228 {
229 Lines: diags.LineRange{First: 1, Last: 3},
230 Error: parser.ParseError{Err: errors.New("missing expr key"), Line: 1},
231 },
232 },
233 },
234 },
235 },
236 },
237 {
238 input: []byte("- record: - foo\n"),
239 output: parser.File{
240 IsRelaxed: true,
241 Error: parser.ParseError{
242 Err: errors.New("yaml: block sequence entries are not allowed in this context"),
243 Line: 1,
244 },
245 },
246 },
247 {
248 input: []byte("- record: foo expr: sum(\n"),
249 output: parser.File{
250 IsRelaxed: true,
251 Error: parser.ParseError{
252 Err: errors.New("yaml: mapping values are not allowed in this context"),
253 Line: 1,
254 },
255 },
256 },
257 {
258 input: []byte("- record\n\texpr: foo\n"),
259 output: parser.File{
260 IsRelaxed: true,
261 Error: parser.ParseError{
262 Err: errors.New("found a tab character that violates indentation"),
263 Line: 2,
264 },
265 },
266 },
267 {
268 input: []byte(`
269- record: foo
270 expr: bar
271 expr: bar
272`),
273 output: parser.File{
274 IsRelaxed: true,
275 Groups: []parser.Group{
276 {
277 Rules: []parser.Rule{
278 {
279 Lines: diags.LineRange{First: 2, Last: 4},
280 Error: parser.ParseError{Err: errors.New("duplicated expr key"), Line: 4},
281 },
282 },
283 },
284 },
285 },
286 },
287 {
288 input: []byte(`
289- record: foo
290 expr: bar
291 record: bar
292`),
293 output: parser.File{
294 IsRelaxed: true,
295 Groups: []parser.Group{
296 {
297 Rules: []parser.Rule{
298 {
299 Lines: diags.LineRange{First: 2, Last: 4},
300 Error: parser.ParseError{Err: errors.New("duplicated record key"), Line: 4},
301 },
302 },
303 },
304 },
305 },
306 },
307 {
308 input: []byte(`
309- alert: foo
310 alert: bar
311 expr: bar
312`),
313 output: parser.File{
314 IsRelaxed: true,
315 Groups: []parser.Group{
316 {
317 Rules: []parser.Rule{
318 {
319 Lines: diags.LineRange{First: 2, Last: 3},
320 Error: parser.ParseError{Err: errors.New("duplicated alert key"), Line: 3},
321 },
322 },
323 },
324 },
325 },
326 },
327 {
328 input: []byte(`
329- alert: foo
330 for: 5m
331 expr: bar
332 for: 1m
333`),
334 output: parser.File{
335 IsRelaxed: true,
336 Groups: []parser.Group{
337 {
338 Rules: []parser.Rule{
339 {
340 Lines: diags.LineRange{First: 2, Last: 5},
341 Error: parser.ParseError{Err: errors.New("duplicated for key"), Line: 5},
342 },
343 },
344 },
345 },
346 },
347 },
348 {
349 input: []byte(`
350- alert: foo
351 keep_firing_for: 5m
352 expr: bar
353 keep_firing_for: 1m
354`),
355 output: parser.File{
356 IsRelaxed: true,
357 Groups: []parser.Group{
358 {
359 Rules: []parser.Rule{
360 {
361 Lines: diags.LineRange{First: 2, Last: 5},
362 Error: parser.ParseError{Err: errors.New("duplicated keep_firing_for key"), Line: 5},
363 },
364 },
365 },
366 },
367 },
368 },
369 {
370 input: []byte(`
371- alert: foo
372 labels: {}
373 expr: bar
374 labels: {}
375`),
376 output: parser.File{
377 IsRelaxed: true,
378 Groups: []parser.Group{
379 {
380 Rules: []parser.Rule{
381 {
382 Lines: diags.LineRange{First: 2, Last: 5},
383 Error: parser.ParseError{Err: errors.New("duplicated labels key"), Line: 5},
384 },
385 },
386 },
387 },
388 },
389 },
390 {
391 input: []byte(`
392- record: foo
393 labels: {}
394 expr: bar
395 labels: {}
396`),
397 output: parser.File{
398 IsRelaxed: true,
399 Groups: []parser.Group{
400 {
401 Rules: []parser.Rule{
402 {
403 Lines: diags.LineRange{First: 2, Last: 5},
404 Error: parser.ParseError{Err: errors.New("duplicated labels key"), Line: 5},
405 },
406 },
407 },
408 },
409 },
410 },
411 {
412 input: []byte(`
413- alert: foo
414 annotations: {}
415 expr: bar
416 annotations: {}
417`),
418 output: parser.File{
419 IsRelaxed: true,
420 Groups: []parser.Group{
421 {
422 Rules: []parser.Rule{
423 {
424 Lines: diags.LineRange{First: 2, Last: 5},
425 Error: parser.ParseError{Err: errors.New("duplicated annotations key"), Line: 5},
426 },
427 },
428 },
429 },
430 },
431 },
432 {
433 input: []byte("- record: foo\n expr: foo\n extra: true\n"),
434 output: parser.File{
435 IsRelaxed: true,
436 Groups: []parser.Group{
437 {
438 Rules: []parser.Rule{
439 {
440 Lines: diags.LineRange{First: 1, Last: 3},
441 Error: parser.ParseError{Err: errors.New("invalid key(s) found: extra"), Line: 3},
442 },
443 },
444 },
445 },
446 },
447 },
448 {
449 input: []byte(`- record: foo
450 expr: foo offset 10m
451`),
452 output: parser.File{
453 IsRelaxed: true,
454 Groups: []parser.Group{
455 {
456 Rules: []parser.Rule{
457 {
458 Lines: diags.LineRange{First: 1, Last: 2},
459 RecordingRule: &parser.RecordingRule{
460 Record: parser.YamlNode{
461 Value: "foo",
462 Pos: diags.PositionRanges{
463 {Line: 1, FirstColumn: 11, LastColumn: 13},
464 },
465 },
466 Expr: parser.PromQLExpr{
467 Value: &parser.YamlNode{
468 Pos: diags.PositionRanges{
469 {Line: 2, FirstColumn: 9, LastColumn: 22},
470 },
471 Value: "foo offset 10m",
472 },
473 },
474 },
475 },
476 },
477 },
478 },
479 },
480 },
481 {
482 input: []byte("- record: foo\n expr: foo offset -10m\n"),
483 output: parser.File{
484 IsRelaxed: true,
485 Groups: []parser.Group{
486 {
487 Rules: []parser.Rule{
488 {
489 Lines: diags.LineRange{First: 1, Last: 2},
490 RecordingRule: &parser.RecordingRule{
491 Record: parser.YamlNode{
492 Value: "foo",
493 Pos: diags.PositionRanges{
494 {Line: 1, FirstColumn: 11, LastColumn: 13},
495 },
496 },
497 Expr: parser.PromQLExpr{
498 Value: &parser.YamlNode{
499 Value: "foo offset -10m",
500 Pos: diags.PositionRanges{
501 {Line: 2, FirstColumn: 9, LastColumn: 23},
502 },
503 },
504 },
505 },
506 },
507 },
508 },
509 },
510 },
511 },
512 {
513 input: []byte(`
514# pint disable head comment
515- record: foo # pint disable record comment
516 expr: foo offset 10m # pint disable expr comment
517 # pint disable pre-labels comment
518 labels:
519 # pint disable pre-foo comment
520 foo: bar
521 # pint disable post-foo comment
522 bob: alice
523 # pint disable foot comment
524`),
525 output: parser.File{
526 IsRelaxed: true,
527 Groups: []parser.Group{
528 {
529 Rules: []parser.Rule{
530 {
531 Lines: diags.LineRange{First: 3, Last: 10},
532 Comments: []comments.Comment{
533 {
534 Type: comments.DisableType,
535 Value: comments.Disable{
536 Match: "head comment",
537 Position: comments.Position{
538 Offset: 15,
539 Pos: diags.PositionRanges{
540 {Line: 2, FirstColumn: 1, LastColumn: 27},
541 },
542 },
543 },
544 },
545 {
546 Type: comments.DisableType,
547 Value: comments.Disable{
548 Match: "record comment",
549 Position: comments.Position{
550 Offset: 15,
551 Pos: diags.PositionRanges{
552 {Line: 3, FirstColumn: 1, LastColumn: 29},
553 },
554 },
555 },
556 },
557 {
558 Type: comments.DisableType,
559 Value: comments.Disable{
560 Match: "expr comment",
561 Position: comments.Position{
562 Offset: 15,
563 Pos: diags.PositionRanges{
564 {Line: 4, FirstColumn: 1, LastColumn: 27},
565 },
566 },
567 },
568 },
569 {
570 Type: comments.DisableType,
571 Value: comments.Disable{
572 Match: "pre-labels comment",
573 Position: comments.Position{
574 Offset: 16,
575 Pos: diags.PositionRanges{
576 {Line: 5, FirstColumn: 3, LastColumn: 36},
577 },
578 },
579 },
580 },
581 {
582 Type: comments.DisableType,
583 Value: comments.Disable{
584 Match: "foot comment",
585 Position: comments.Position{
586 Offset: 15,
587 Pos: diags.PositionRanges{
588 {Line: 7, FirstColumn: 5, LastColumn: 31},
589 },
590 },
591 },
592 },
593 {
594 Type: comments.DisableType,
595 Value: comments.Disable{
596 Match: "pre-foo comment",
597 Position: comments.Position{
598 Offset: 15,
599 Pos: diags.PositionRanges{
600 {Line: 7, FirstColumn: 5, LastColumn: 34},
601 },
602 },
603 },
604 },
605 {
606 Type: comments.DisableType,
607 Value: comments.Disable{
608 Match: "post-foo comment",
609 Position: comments.Position{
610 Offset: 15,
611 Pos: diags.PositionRanges{
612 {Line: 9, FirstColumn: 5, LastColumn: 35},
613 },
614 },
615 },
616 },
617 },
618 RecordingRule: &parser.RecordingRule{
619 Record: parser.YamlNode{
620 Value: "foo",
621 Pos: diags.PositionRanges{
622 {Line: 3, FirstColumn: 11, LastColumn: 13},
623 },
624 },
625 Expr: parser.PromQLExpr{
626 Value: &parser.YamlNode{
627 Value: "foo offset 10m",
628 Pos: diags.PositionRanges{
629 {Line: 4, FirstColumn: 9, LastColumn: 22},
630 },
631 },
632 },
633 Labels: &parser.YamlMap{
634 Key: &parser.YamlNode{
635 Value: "labels",
636 Pos: diags.PositionRanges{
637 {Line: 6, FirstColumn: 3, LastColumn: 8},
638 },
639 },
640 Items: []*parser.YamlKeyValue{
641 {
642 Key: &parser.YamlNode{
643 Value: "foo",
644 Pos: diags.PositionRanges{
645 {Line: 8, FirstColumn: 5, LastColumn: 7},
646 },
647 },
648 Value: &parser.YamlNode{
649 Value: "bar",
650 Pos: diags.PositionRanges{
651 {Line: 8, FirstColumn: 10, LastColumn: 12},
652 },
653 },
654 },
655 {
656 Key: &parser.YamlNode{
657 Value: "bob",
658 Pos: diags.PositionRanges{
659 {Line: 10, FirstColumn: 5, LastColumn: 7},
660 },
661 },
662 Value: &parser.YamlNode{
663 Value: "alice",
664 Pos: diags.PositionRanges{
665 {Line: 10, FirstColumn: 10, LastColumn: 14},
666 },
667 },
668 },
669 },
670 },
671 },
672 },
673 },
674 },
675 },
676 },
677 },
678 {
679 input: []byte("- record: foo\n expr: foo[5m] offset 10m\n"),
680 output: parser.File{
681 IsRelaxed: true,
682 Groups: []parser.Group{
683 {
684 Rules: []parser.Rule{
685 {
686 Lines: diags.LineRange{First: 1, Last: 2},
687 RecordingRule: &parser.RecordingRule{
688 Record: parser.YamlNode{
689 Value: "foo",
690 Pos: diags.PositionRanges{
691 {Line: 1, FirstColumn: 11, LastColumn: 13},
692 },
693 },
694 Expr: parser.PromQLExpr{
695 Value: &parser.YamlNode{
696 Value: "foo[5m] offset 10m",
697 Pos: diags.PositionRanges{
698 {Line: 2, FirstColumn: 9, LastColumn: 26},
699 },
700 },
701 },
702 },
703 },
704 },
705 },
706 },
707 },
708 },
709 {
710 input: []byte(`
711- record: name
712 expr: sum(foo)
713 labels:
714 foo: bar
715 bob: alice
716`),
717 output: parser.File{
718 IsRelaxed: true,
719 Groups: []parser.Group{
720 {
721 Rules: []parser.Rule{
722 {
723 Lines: diags.LineRange{First: 2, Last: 6},
724 RecordingRule: &parser.RecordingRule{
725 Record: parser.YamlNode{
726 Value: "name",
727 Pos: diags.PositionRanges{
728 {Line: 2, FirstColumn: 11, LastColumn: 14},
729 },
730 },
731 Expr: parser.PromQLExpr{
732 Value: &parser.YamlNode{
733 Value: "sum(foo)",
734 Pos: diags.PositionRanges{
735 {Line: 3, FirstColumn: 9, LastColumn: 16},
736 },
737 },
738 },
739 Labels: &parser.YamlMap{
740 Key: &parser.YamlNode{
741 Value: "labels",
742 Pos: diags.PositionRanges{
743 {Line: 4, FirstColumn: 3, LastColumn: 8},
744 },
745 },
746 Items: []*parser.YamlKeyValue{
747 {
748 Key: &parser.YamlNode{
749 Value: "foo",
750 Pos: diags.PositionRanges{
751 {Line: 5, FirstColumn: 5, LastColumn: 7},
752 },
753 },
754 Value: &parser.YamlNode{
755 Value: "bar",
756 Pos: diags.PositionRanges{
757 {Line: 5, FirstColumn: 10, LastColumn: 12},
758 },
759 },
760 },
761 {
762 Key: &parser.YamlNode{
763 Value: "bob",
764 Pos: diags.PositionRanges{
765 {Line: 6, FirstColumn: 5, LastColumn: 7},
766 },
767 },
768 Value: &parser.YamlNode{
769 Value: "alice",
770 Pos: diags.PositionRanges{
771 {Line: 6, FirstColumn: 10, LastColumn: 14},
772 },
773 },
774 },
775 },
776 },
777 },
778 },
779 },
780 },
781 },
782 },
783 },
784 {
785 input: []byte(`
786groups:
787- name: custom_rules
788 rules:
789 - record: name
790 expr: sum(foo)
791 labels:
792 foo: bar
793 bob: alice
794`),
795 output: parser.File{
796 IsRelaxed: true,
797 Groups: []parser.Group{
798 {
799 Name: parser.YamlNode{
800 Value: "custom_rules",
801 Pos: diags.PositionRanges{
802 {Line: 3, FirstColumn: 9, LastColumn: 20},
803 },
804 },
805 Rules: []parser.Rule{
806 {
807 Lines: diags.LineRange{First: 5, Last: 9},
808 RecordingRule: &parser.RecordingRule{
809 Record: parser.YamlNode{
810 Value: "name",
811 Pos: diags.PositionRanges{
812 {Line: 5, FirstColumn: 15, LastColumn: 18},
813 },
814 },
815 Expr: parser.PromQLExpr{
816 Value: &parser.YamlNode{
817 Value: "sum(foo)",
818 Pos: diags.PositionRanges{
819 {Line: 6, FirstColumn: 13, LastColumn: 20},
820 },
821 },
822 },
823 Labels: &parser.YamlMap{
824 Key: &parser.YamlNode{
825 Value: "labels",
826 Pos: diags.PositionRanges{
827 {Line: 7, FirstColumn: 7, LastColumn: 12},
828 },
829 },
830 Items: []*parser.YamlKeyValue{
831 {
832 Key: &parser.YamlNode{
833 Value: "foo",
834 Pos: diags.PositionRanges{
835 {Line: 8, FirstColumn: 9, LastColumn: 11},
836 },
837 },
838 Value: &parser.YamlNode{
839 Value: "bar",
840 Pos: diags.PositionRanges{
841 {Line: 8, FirstColumn: 14, LastColumn: 16},
842 },
843 },
844 },
845 {
846 Key: &parser.YamlNode{
847 Value: "bob",
848 Pos: diags.PositionRanges{
849 {Line: 9, FirstColumn: 9, LastColumn: 11},
850 },
851 },
852 Value: &parser.YamlNode{
853 Value: "alice",
854 Pos: diags.PositionRanges{
855 {Line: 9, FirstColumn: 14, LastColumn: 18},
856 },
857 },
858 },
859 },
860 },
861 },
862 },
863 },
864 },
865 },
866 },
867 },
868 {
869 input: []byte(`- alert: Down
870 expr: |
871 up == 0
872 for: |+
873 11m
874 labels:
875 severity: critical
876 annotations:
877 uri: https://docs.example.com/down.html
878
879- record: foo
880 expr: |-
881 bar
882 /
883 baz > 1
884 labels: {}
885`),
886 output: parser.File{
887 IsRelaxed: true,
888 Groups: []parser.Group{
889 {
890 Rules: []parser.Rule{
891 {
892 Lines: diags.LineRange{First: 1, Last: 9},
893 AlertingRule: &parser.AlertingRule{
894 Alert: parser.YamlNode{
895 Value: "Down",
896 Pos: diags.PositionRanges{
897 {Line: 1, FirstColumn: 10, LastColumn: 13},
898 },
899 },
900 Expr: parser.PromQLExpr{
901 Value: &parser.YamlNode{
902 Value: "up == 0\n",
903 Pos: diags.PositionRanges{
904 {Line: 3, FirstColumn: 5, LastColumn: 11},
905 },
906 },
907 },
908 For: &parser.YamlDuration{
909 Raw: "11m\n",
910 ParseError: errors.New(`unknown unit "m\n" in duration "11m\n"`),
911 Pos: diags.PositionRanges{
912 {Line: 5, FirstColumn: 5, LastColumn: 7},
913 },
914 },
915 Labels: &parser.YamlMap{
916 Key: &parser.YamlNode{
917 Value: "labels",
918 Pos: diags.PositionRanges{
919 {Line: 6, FirstColumn: 3, LastColumn: 8},
920 },
921 },
922 Items: []*parser.YamlKeyValue{
923 {
924 Key: &parser.YamlNode{
925 Value: "severity",
926 Pos: diags.PositionRanges{
927 {Line: 7, FirstColumn: 5, LastColumn: 12},
928 },
929 },
930 Value: &parser.YamlNode{
931 Value: "critical",
932 Pos: diags.PositionRanges{
933 {Line: 7, FirstColumn: 15, LastColumn: 22},
934 },
935 },
936 },
937 },
938 },
939 Annotations: &parser.YamlMap{
940 Key: &parser.YamlNode{
941 Value: "annotations",
942 Pos: diags.PositionRanges{
943 {Line: 8, FirstColumn: 3, LastColumn: 13},
944 },
945 },
946 Items: []*parser.YamlKeyValue{
947 {
948 Key: &parser.YamlNode{
949 Value: "uri",
950 Pos: diags.PositionRanges{
951 {Line: 9, FirstColumn: 5, LastColumn: 7},
952 },
953 },
954 Value: &parser.YamlNode{
955 Value: "https://docs.example.com/down.html",
956 Pos: diags.PositionRanges{
957 {Line: 9, FirstColumn: 10, LastColumn: 43},
958 },
959 },
960 },
961 },
962 },
963 },
964 },
965 {
966 Lines: diags.LineRange{First: 11, Last: 16},
967 RecordingRule: &parser.RecordingRule{
968 Record: parser.YamlNode{
969 Value: "foo",
970 Pos: diags.PositionRanges{
971 {Line: 11, FirstColumn: 11, LastColumn: 13},
972 },
973 },
974 Expr: parser.PromQLExpr{
975 Value: &parser.YamlNode{
976 Value: "bar\n/\nbaz > 1",
977 Pos: diags.PositionRanges{
978 {Line: 13, FirstColumn: 5, LastColumn: 8},
979 {Line: 14, FirstColumn: 5, LastColumn: 6},
980 {Line: 15, FirstColumn: 5, LastColumn: 11},
981 },
982 },
983 },
984 Labels: &parser.YamlMap{
985 Key: &parser.YamlNode{
986 Value: "labels",
987 Pos: diags.PositionRanges{
988 {Line: 16, FirstColumn: 3, LastColumn: 8},
989 },
990 },
991 },
992 },
993 },
994 },
995 },
996 },
997 },
998 },
999 {
1000 input: []byte(`- alert: Foo
1001 expr:
1002 (
1003 xxx
1004 -
1005 yyy
1006 ) * bar > 0
1007 and on(instance, device) baz
1008 for: 30m
1009`),
1010 output: parser.File{
1011 IsRelaxed: true,
1012 Groups: []parser.Group{
1013 {
1014 Rules: []parser.Rule{
1015 {
1016 Lines: diags.LineRange{First: 1, Last: 9},
1017 AlertingRule: &parser.AlertingRule{
1018 Alert: parser.YamlNode{
1019 Value: "Foo",
1020 Pos: diags.PositionRanges{
1021 {Line: 1, FirstColumn: 10, LastColumn: 12},
1022 },
1023 },
1024 Expr: parser.PromQLExpr{
1025 Value: &parser.YamlNode{
1026 Value: "( xxx - yyy ) * bar > 0 and on(instance, device) baz",
1027 Pos: diags.PositionRanges{
1028 {Line: 3, FirstColumn: 5, LastColumn: 6},
1029 {Line: 4, FirstColumn: 7, LastColumn: 10},
1030 {Line: 5, FirstColumn: 7, LastColumn: 8},
1031 {Line: 6, FirstColumn: 7, LastColumn: 10},
1032 {Line: 7, FirstColumn: 5, LastColumn: 16},
1033 {Line: 8, FirstColumn: 5, LastColumn: 32},
1034 },
1035 },
1036 },
1037 For: &parser.YamlDuration{
1038 Value: time.Minute * 30,
1039 Raw: "30m",
1040 Pos: diags.PositionRanges{
1041 {Line: 9, FirstColumn: 8, LastColumn: 10},
1042 },
1043 },
1044 },
1045 },
1046 },
1047 },
1048 },
1049 },
1050 },
1051 {
1052 input: []byte(`---
1053kind: ConfigMap
1054apiVersion: v1
1055metadata:
1056 name: example-app-alerts
1057 labels:
1058 app: example-app
1059data:
1060 alerts: |
1061 groups:
1062 - name: example-app-alerts
1063 rules:
1064 - alert: Example_High_Restart_Rate
1065 expr: sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )
1066---
1067kind: ConfigMap
1068apiVersion: v1
1069metadata:
1070 name: other
1071 labels:
1072 app: other
1073data:
1074 alerts: |
1075
1076 groups:
1077 - name: other alerts
1078 rules:
1079 - alert: Example_High_Restart_Rate
1080 expr: "1"
1081
1082`),
1083 output: parser.File{
1084 IsRelaxed: true,
1085 Groups: []parser.Group{
1086 {
1087 Name: parser.YamlNode{
1088 Value: "example-app-alerts",
1089 Pos: diags.PositionRanges{
1090 {Line: 2, FirstColumn: 11, LastColumn: 28},
1091 },
1092 },
1093 Rules: []parser.Rule{
1094 {
1095 Lines: diags.LineRange{First: 13, Last: 14},
1096 AlertingRule: &parser.AlertingRule{
1097 Alert: parser.YamlNode{
1098 Value: "Example_High_Restart_Rate",
1099 Pos: diags.PositionRanges{
1100 {Line: 13, FirstColumn: 20, LastColumn: 44},
1101 },
1102 },
1103 Expr: parser.PromQLExpr{
1104 Value: &parser.YamlNode{
1105 Value: `sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )`,
1106 Pos: diags.PositionRanges{
1107 {Line: 14, FirstColumn: 19, LastColumn: 109},
1108 },
1109 },
1110 },
1111 },
1112 },
1113 },
1114 },
1115 {
1116 Name: parser.YamlNode{
1117 Value: "other alerts",
1118 Pos: diags.PositionRanges{
1119 {Line: 3, FirstColumn: 11, LastColumn: 22},
1120 },
1121 },
1122 Rules: []parser.Rule{
1123 {
1124 Lines: diags.LineRange{First: 28, Last: 29},
1125 AlertingRule: &parser.AlertingRule{
1126 Alert: parser.YamlNode{
1127 Value: "Example_High_Restart_Rate",
1128 Pos: diags.PositionRanges{
1129 {Line: 28, FirstColumn: 16, LastColumn: 40},
1130 },
1131 },
1132 Expr: parser.PromQLExpr{
1133 Value: &parser.YamlNode{
1134 Value: "1",
1135 Pos: diags.PositionRanges{
1136 {Line: 29, FirstColumn: 16, LastColumn: 16},
1137 },
1138 },
1139 },
1140 },
1141 },
1142 },
1143 },
1144 },
1145 },
1146 },
1147 {
1148 input: []byte(`---
1149kind: ConfigMap
1150apiVersion: v1
1151metadata:
1152 name: example-app-alerts
1153 labels:
1154 app: example-app
1155data:
1156 alerts: |
1157 groups:
1158 - name: example-app-alerts
1159 rules:
1160 - alert: Example_Is_Down
1161 expr: kube_deployment_status_replicas_available{namespace="example-app"} < 1
1162 for: 5m
1163 labels:
1164 priority: "2"
1165 environment: production
1166 annotations:
1167 summary: "No replicas for Example have been running for 5 minutes"
1168
1169 - alert: Example_High_Restart_Rate
1170 expr: sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )
1171`),
1172 output: parser.File{
1173 IsRelaxed: true,
1174 Groups: []parser.Group{
1175 {
1176 Name: parser.YamlNode{
1177 Value: "example-app-alerts",
1178 Pos: diags.PositionRanges{
1179 {Line: 2, FirstColumn: 11, LastColumn: 28},
1180 },
1181 },
1182 Rules: []parser.Rule{
1183 {
1184 Lines: diags.LineRange{First: 13, Last: 20},
1185 AlertingRule: &parser.AlertingRule{
1186 Alert: parser.YamlNode{
1187 Value: "Example_Is_Down",
1188 Pos: diags.PositionRanges{
1189 {Line: 13, FirstColumn: 20, LastColumn: 34},
1190 },
1191 },
1192 Expr: parser.PromQLExpr{
1193 Value: &parser.YamlNode{
1194 Value: `kube_deployment_status_replicas_available{namespace="example-app"} < 1`,
1195 Pos: diags.PositionRanges{
1196 {Line: 14, FirstColumn: 19, LastColumn: 88},
1197 },
1198 },
1199 },
1200 For: &parser.YamlDuration{
1201 Value: time.Minute * 5,
1202 Raw: "5m",
1203 Pos: diags.PositionRanges{
1204 {Line: 15, FirstColumn: 18, LastColumn: 19},
1205 },
1206 },
1207 Labels: &parser.YamlMap{
1208 Key: &parser.YamlNode{
1209 Value: "labels",
1210 Pos: diags.PositionRanges{
1211 {Line: 16, FirstColumn: 13, LastColumn: 18},
1212 },
1213 },
1214 Items: []*parser.YamlKeyValue{
1215 {
1216 Key: &parser.YamlNode{
1217 Value: "priority",
1218 Pos: diags.PositionRanges{
1219 {Line: 17, FirstColumn: 15, LastColumn: 22},
1220 },
1221 },
1222 Value: &parser.YamlNode{
1223 Value: "2",
1224 Pos: diags.PositionRanges{
1225 {Line: 17, FirstColumn: 26, LastColumn: 26},
1226 },
1227 },
1228 },
1229 {
1230 Key: &parser.YamlNode{
1231 Value: "environment",
1232 Pos: diags.PositionRanges{
1233 {Line: 18, FirstColumn: 15, LastColumn: 25},
1234 },
1235 },
1236 Value: &parser.YamlNode{
1237 Value: "production",
1238 Pos: diags.PositionRanges{
1239 {Line: 18, FirstColumn: 28, LastColumn: 37},
1240 },
1241 },
1242 },
1243 },
1244 },
1245 Annotations: &parser.YamlMap{
1246 Key: &parser.YamlNode{
1247 Value: "annotations",
1248 Pos: diags.PositionRanges{
1249 {Line: 19, FirstColumn: 13, LastColumn: 23},
1250 },
1251 },
1252 Items: []*parser.YamlKeyValue{
1253 {
1254 Key: &parser.YamlNode{
1255 Value: "summary",
1256 Pos: diags.PositionRanges{
1257 {Line: 20, FirstColumn: 15, LastColumn: 21},
1258 },
1259 },
1260 Value: &parser.YamlNode{
1261 Value: "No replicas for Example have been running for 5 minutes",
1262 Pos: diags.PositionRanges{
1263 {Line: 20, FirstColumn: 25, LastColumn: 79},
1264 },
1265 },
1266 },
1267 },
1268 },
1269 },
1270 },
1271 {
1272 Lines: diags.LineRange{First: 22, Last: 23},
1273 AlertingRule: &parser.AlertingRule{
1274 Alert: parser.YamlNode{
1275 Value: "Example_High_Restart_Rate",
1276 Pos: diags.PositionRanges{
1277 {Line: 22, FirstColumn: 20, LastColumn: 44},
1278 },
1279 },
1280 Expr: parser.PromQLExpr{
1281 Value: &parser.YamlNode{
1282 Value: `sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )`,
1283 Pos: diags.PositionRanges{
1284 {Line: 23, FirstColumn: 19, LastColumn: 109},
1285 },
1286 },
1287 },
1288 },
1289 },
1290 },
1291 },
1292 },
1293 },
1294 },
1295 {
1296 input: []byte(`groups:
1297- name: "haproxy.api_server.rules"
1298 rules:
1299 - alert: HaproxyServerHealthcheckFailure
1300 expr: increase(haproxy_server_check_failures_total[15m]) > 100
1301 for: 5m
1302 labels:
1303 severity: 24x7
1304 annotations:
1305 summary: "HAProxy server healthcheck failure (instance {{ $labels.instance }})"
1306 description: "Some server healthcheck are failing on {{ $labels.server }}\n VALUE = {{ $value }}\n LABELS: {{ $labels }}"
1307`),
1308 output: parser.File{
1309 IsRelaxed: true,
1310 Groups: []parser.Group{
1311 {
1312 Name: parser.YamlNode{
1313 Value: "haproxy.api_server.rules",
1314 Pos: diags.PositionRanges{
1315 {Line: 2, FirstColumn: 10, LastColumn: 33},
1316 },
1317 },
1318 Rules: []parser.Rule{
1319 {
1320 Lines: diags.LineRange{First: 4, Last: 11},
1321 AlertingRule: &parser.AlertingRule{
1322 Alert: parser.YamlNode{
1323 Value: "HaproxyServerHealthcheckFailure",
1324 Pos: diags.PositionRanges{
1325 {Line: 4, FirstColumn: 12, LastColumn: 42},
1326 },
1327 },
1328 Expr: parser.PromQLExpr{
1329 Value: &parser.YamlNode{
1330 Value: "increase(haproxy_server_check_failures_total[15m]) > 100",
1331 Pos: diags.PositionRanges{
1332 {Line: 5, FirstColumn: 11, LastColumn: 66},
1333 },
1334 },
1335 },
1336 For: &parser.YamlDuration{
1337 Value: time.Minute * 5,
1338 Raw: "5m",
1339 Pos: diags.PositionRanges{
1340 {Line: 6, FirstColumn: 10, LastColumn: 11},
1341 },
1342 },
1343 Labels: &parser.YamlMap{
1344 Key: &parser.YamlNode{
1345 Value: "labels",
1346 Pos: diags.PositionRanges{
1347 {Line: 7, FirstColumn: 5, LastColumn: 10},
1348 },
1349 },
1350 Items: []*parser.YamlKeyValue{
1351 {
1352 Key: &parser.YamlNode{
1353 Value: "severity",
1354 Pos: diags.PositionRanges{
1355 {Line: 8, FirstColumn: 7, LastColumn: 14},
1356 },
1357 },
1358 Value: &parser.YamlNode{
1359 Value: "24x7",
1360 Pos: diags.PositionRanges{
1361 {Line: 8, FirstColumn: 17, LastColumn: 20},
1362 },
1363 },
1364 },
1365 },
1366 },
1367 Annotations: &parser.YamlMap{
1368 Key: &parser.YamlNode{
1369 Value: "annotations",
1370 Pos: diags.PositionRanges{
1371 {Line: 9, FirstColumn: 5, LastColumn: 15},
1372 },
1373 },
1374 Items: []*parser.YamlKeyValue{
1375 {
1376 Key: &parser.YamlNode{
1377 Value: "summary",
1378 Pos: diags.PositionRanges{
1379 {Line: 10, FirstColumn: 7, LastColumn: 13},
1380 },
1381 },
1382 Value: &parser.YamlNode{
1383 Value: "HAProxy server healthcheck failure (instance {{ $labels.instance }})",
1384 Pos: diags.PositionRanges{
1385 {Line: 10, FirstColumn: 17, LastColumn: 84},
1386 },
1387 },
1388 },
1389 {
1390 Key: &parser.YamlNode{
1391 Value: "description",
1392 Pos: diags.PositionRanges{
1393 {Line: 11, FirstColumn: 7, LastColumn: 17},
1394 },
1395 },
1396 Value: &parser.YamlNode{
1397 Value: "Some server healthcheck are failing on {{ $labels.server }}\n VALUE = {{ $value }}\n LABELS: {{ $labels }}",
1398 Pos: diags.PositionRanges{
1399 {Line: 11, FirstColumn: 21, LastColumn: 79},
1400 },
1401 },
1402 },
1403 },
1404 },
1405 },
1406 },
1407 },
1408 },
1409 },
1410 },
1411 },
1412 {
1413 input: []byte(`groups:
1414- name: certmanager
1415 rules:
1416 # pint disable before recordAnchor
1417 - &recordAnchor # pint disable recordAnchor
1418 record: name1 # pint disable name1
1419 expr: expr1 # pint disable expr1
1420 # pint disable after expr1
1421 - <<: *recordAnchor
1422 expr: expr2
1423 - <<: *recordAnchor
1424`),
1425 output: parser.File{
1426 IsRelaxed: true,
1427 Groups: []parser.Group{
1428 {
1429 Name: parser.YamlNode{
1430 Value: "certmanager",
1431 Pos: diags.PositionRanges{
1432 {Line: 2, FirstColumn: 9, LastColumn: 19},
1433 },
1434 },
1435 Rules: []parser.Rule{
1436 {
1437 Lines: diags.LineRange{First: 6, Last: 7},
1438 Comments: []comments.Comment{
1439 {
1440 Type: comments.DisableType,
1441 Value: comments.Disable{
1442 Match: "before recordAnchor",
1443 Position: comments.Position{
1444 Offset: 15,
1445 Pos: diags.PositionRanges{
1446 {Line: 5, FirstColumn: 3, LastColumn: 36},
1447 },
1448 },
1449 },
1450 },
1451 {
1452 Type: comments.DisableType,
1453 Value: comments.Disable{
1454 Match: "recordAnchor",
1455 Position: comments.Position{
1456 Offset: 15,
1457 Pos: diags.PositionRanges{
1458 {Line: 6, FirstColumn: 1, LastColumn: 27},
1459 },
1460 },
1461 },
1462 },
1463 {
1464 Type: comments.DisableType,
1465 Value: comments.Disable{
1466 Match: "name1",
1467 Position: comments.Position{
1468 Offset: 15,
1469 Pos: diags.PositionRanges{
1470 {Line: 6, FirstColumn: 1, LastColumn: 20},
1471 },
1472 },
1473 },
1474 },
1475 {
1476 Type: comments.DisableType,
1477 Value: comments.Disable{
1478 Match: "after expr1",
1479 Position: comments.Position{
1480 Offset: 15,
1481 Pos: diags.PositionRanges{
1482 {Line: 8, FirstColumn: 5, LastColumn: 30},
1483 },
1484 },
1485 },
1486 },
1487 {
1488 Type: comments.DisableType,
1489 Value: comments.Disable{
1490 Match: "expr1",
1491 Position: comments.Position{
1492 Offset: 15,
1493 Pos: diags.PositionRanges{
1494 {Line: 7, FirstColumn: 1, LastColumn: 20},
1495 },
1496 },
1497 },
1498 },
1499 },
1500 RecordingRule: &parser.RecordingRule{
1501 Record: parser.YamlNode{
1502 Value: "name1",
1503 Pos: diags.PositionRanges{
1504 {Line: 6, FirstColumn: 13, LastColumn: 17},
1505 },
1506 },
1507 Expr: parser.PromQLExpr{
1508 Value: &parser.YamlNode{
1509 Value: "expr1",
1510 Pos: diags.PositionRanges{
1511 {Line: 7, FirstColumn: 11, LastColumn: 15},
1512 },
1513 },
1514 },
1515 },
1516 },
1517 {
1518 Comments: []comments.Comment{
1519 {
1520 Type: comments.DisableType,
1521 Value: comments.Disable{
1522 Match: "before recordAnchor",
1523 Position: comments.Position{
1524 Offset: 15,
1525 Pos: diags.PositionRanges{
1526 {Line: 5, FirstColumn: 3, LastColumn: 36},
1527 },
1528 },
1529 },
1530 },
1531 {
1532 Type: comments.DisableType,
1533 Value: comments.Disable{
1534 Match: "recordAnchor",
1535 Position: comments.Position{
1536 Offset: 15,
1537 Pos: diags.PositionRanges{
1538 {Line: 6, FirstColumn: 1, LastColumn: 27},
1539 },
1540 },
1541 },
1542 },
1543 {
1544 Type: comments.DisableType,
1545 Value: comments.Disable{
1546 Match: "name1",
1547 Position: comments.Position{
1548 Offset: 15,
1549 Pos: diags.PositionRanges{
1550 {Line: 6, FirstColumn: 1, LastColumn: 20},
1551 },
1552 },
1553 },
1554 },
1555 },
1556 Lines: diags.LineRange{First: 6, Last: 10},
1557 RecordingRule: &parser.RecordingRule{
1558 Record: parser.YamlNode{
1559 Value: "name1",
1560 Pos: diags.PositionRanges{
1561 {Line: 6, FirstColumn: 13, LastColumn: 17},
1562 },
1563 },
1564 Expr: parser.PromQLExpr{
1565 Value: &parser.YamlNode{
1566 Value: "expr2",
1567 Pos: diags.PositionRanges{
1568 {Line: 10, FirstColumn: 11, LastColumn: 15},
1569 },
1570 },
1571 },
1572 },
1573 },
1574 {
1575 Comments: []comments.Comment{
1576 {
1577 Type: comments.DisableType,
1578 Value: comments.Disable{
1579 Match: "before recordAnchor",
1580 Position: comments.Position{
1581 Offset: 15,
1582 Pos: diags.PositionRanges{
1583 {Line: 5, FirstColumn: 3, LastColumn: 36},
1584 },
1585 },
1586 },
1587 },
1588 {
1589 Type: comments.DisableType,
1590 Value: comments.Disable{
1591 Match: "recordAnchor",
1592 Position: comments.Position{
1593 Offset: 15,
1594 Pos: diags.PositionRanges{
1595 {Line: 6, FirstColumn: 1, LastColumn: 27},
1596 },
1597 },
1598 },
1599 },
1600 {
1601 Type: comments.DisableType,
1602 Value: comments.Disable{
1603 Match: "name1",
1604 Position: comments.Position{
1605 Offset: 15,
1606 Pos: diags.PositionRanges{
1607 {Line: 6, FirstColumn: 1, LastColumn: 20},
1608 },
1609 },
1610 },
1611 },
1612 {
1613 Type: comments.DisableType,
1614 Value: comments.Disable{
1615 Match: "after expr1",
1616 Position: comments.Position{
1617 Offset: 15,
1618 Pos: diags.PositionRanges{
1619 {Line: 8, FirstColumn: 5, LastColumn: 30},
1620 },
1621 },
1622 },
1623 },
1624 {
1625 Type: comments.DisableType,
1626 Value: comments.Disable{
1627 Match: "expr1",
1628 Position: comments.Position{
1629 Offset: 15,
1630 Pos: diags.PositionRanges{
1631 {Line: 7, FirstColumn: 1, LastColumn: 20},
1632 },
1633 },
1634 },
1635 },
1636 },
1637 Lines: diags.LineRange{First: 6, Last: 7},
1638 RecordingRule: &parser.RecordingRule{
1639 Record: parser.YamlNode{
1640 Value: "name1",
1641 Pos: diags.PositionRanges{
1642 {Line: 6, FirstColumn: 13, LastColumn: 17},
1643 },
1644 },
1645 Expr: parser.PromQLExpr{
1646 Value: &parser.YamlNode{
1647 Value: "expr1",
1648 Pos: diags.PositionRanges{
1649 {Line: 7, FirstColumn: 11, LastColumn: 15},
1650 },
1651 },
1652 },
1653 },
1654 },
1655 },
1656 },
1657 },
1658 },
1659 },
1660 {
1661 input: []byte(`groups:
1662- name: certmanager
1663 rules:
1664 - record: name1
1665 expr: expr1
1666 labels: &labelsAnchor
1667 label1: val1
1668 label2: val2
1669 - record: name2
1670 expr: expr2
1671 labels: *labelsAnchor
1672 # pint disable foot comment
1673`),
1674 output: parser.File{
1675 IsRelaxed: true,
1676 Groups: []parser.Group{
1677 {
1678 Name: parser.YamlNode{
1679 Value: "certmanager",
1680 Pos: diags.PositionRanges{
1681 {Line: 2, FirstColumn: 9, LastColumn: 19},
1682 },
1683 },
1684 Rules: []parser.Rule{
1685 {
1686 Lines: diags.LineRange{First: 4, Last: 8},
1687 RecordingRule: &parser.RecordingRule{
1688 Record: parser.YamlNode{
1689 Value: "name1",
1690 Pos: diags.PositionRanges{
1691 {Line: 4, FirstColumn: 13, LastColumn: 17},
1692 },
1693 },
1694 Expr: parser.PromQLExpr{
1695 Value: &parser.YamlNode{
1696 Value: "expr1",
1697 Pos: diags.PositionRanges{
1698 {Line: 5, FirstColumn: 11, LastColumn: 15},
1699 },
1700 },
1701 },
1702 Labels: &parser.YamlMap{
1703 Key: &parser.YamlNode{
1704 Value: "labels",
1705 Pos: diags.PositionRanges{
1706 {Line: 6, FirstColumn: 5, LastColumn: 10},
1707 },
1708 },
1709 Items: []*parser.YamlKeyValue{
1710 {
1711 Key: &parser.YamlNode{
1712 Value: "label1",
1713 Pos: diags.PositionRanges{
1714 {Line: 7, FirstColumn: 7, LastColumn: 12},
1715 },
1716 },
1717 Value: &parser.YamlNode{
1718 Value: "val1",
1719 Pos: diags.PositionRanges{
1720 {Line: 7, FirstColumn: 15, LastColumn: 18},
1721 },
1722 },
1723 },
1724 {
1725 Key: &parser.YamlNode{
1726 Value: "label2",
1727 Pos: diags.PositionRanges{
1728 {Line: 8, FirstColumn: 7, LastColumn: 12},
1729 },
1730 },
1731 Value: &parser.YamlNode{
1732 Value: "val2",
1733 Pos: diags.PositionRanges{
1734 {Line: 8, FirstColumn: 15, LastColumn: 18},
1735 },
1736 },
1737 },
1738 },
1739 },
1740 },
1741 },
1742 {
1743 Comments: []comments.Comment{
1744 {
1745 Type: comments.DisableType,
1746 Value: comments.Disable{
1747 Match: "foot comment",
1748 Position: comments.Position{
1749 Offset: 15,
1750 Pos: diags.PositionRanges{
1751 {Line: 12, FirstColumn: 5, LastColumn: 31},
1752 },
1753 },
1754 },
1755 },
1756 },
1757 Lines: diags.LineRange{First: 9, Last: 11},
1758 RecordingRule: &parser.RecordingRule{
1759 Record: parser.YamlNode{
1760 Value: "name2",
1761 Pos: diags.PositionRanges{
1762 {Line: 9, FirstColumn: 13, LastColumn: 17},
1763 },
1764 },
1765 Expr: parser.PromQLExpr{
1766 Value: &parser.YamlNode{
1767 Value: "expr2",
1768 Pos: diags.PositionRanges{
1769 {Line: 10, FirstColumn: 11, LastColumn: 15},
1770 },
1771 },
1772 },
1773 Labels: &parser.YamlMap{
1774 Key: &parser.YamlNode{
1775 Value: "labels",
1776 Pos: diags.PositionRanges{
1777 {Line: 11, FirstColumn: 5, LastColumn: 10},
1778 },
1779 },
1780 Items: []*parser.YamlKeyValue{
1781 {
1782 Key: &parser.YamlNode{
1783 Value: "label1",
1784 Pos: diags.PositionRanges{
1785 {Line: 7, FirstColumn: 7, LastColumn: 12},
1786 },
1787 },
1788 Value: &parser.YamlNode{
1789 Value: "val1",
1790 Pos: diags.PositionRanges{
1791 {Line: 7, FirstColumn: 15, LastColumn: 18},
1792 },
1793 },
1794 },
1795 {
1796 Key: &parser.YamlNode{
1797 Value: "label2",
1798 Pos: diags.PositionRanges{
1799 {Line: 8, FirstColumn: 7, LastColumn: 12},
1800 },
1801 },
1802 Value: &parser.YamlNode{
1803 Value: "val2",
1804 Pos: diags.PositionRanges{
1805 {Line: 8, FirstColumn: 15, LastColumn: 18},
1806 },
1807 },
1808 },
1809 },
1810 },
1811 },
1812 },
1813 },
1814 },
1815 },
1816 },
1817 },
1818 {
1819 input: []byte("- alert:\n expr: vector(1)\n"),
1820 output: parser.File{
1821 IsRelaxed: true,
1822 Groups: []parser.Group{
1823 {
1824 Rules: []parser.Rule{
1825 {
1826 Lines: diags.LineRange{First: 1, Last: 2},
1827 Error: parser.ParseError{Err: errors.New("alert value cannot be empty"), Line: 1},
1828 },
1829 },
1830 },
1831 },
1832 },
1833 },
1834 {
1835 input: []byte("- alert: foo\n expr:\n"),
1836 output: parser.File{
1837 IsRelaxed: true,
1838 Groups: []parser.Group{
1839 {
1840 Rules: []parser.Rule{
1841 {
1842 Lines: diags.LineRange{First: 1, Last: 2},
1843 Error: parser.ParseError{Err: errors.New("expr value cannot be empty"), Line: 2},
1844 },
1845 },
1846 },
1847 },
1848 },
1849 },
1850 {
1851 input: []byte("- alert: foo\n"),
1852 output: parser.File{
1853 IsRelaxed: true,
1854 Groups: []parser.Group{
1855 {
1856 Rules: []parser.Rule{
1857 {
1858 Lines: diags.LineRange{First: 1, Last: 1},
1859 Error: parser.ParseError{Err: errors.New("missing expr key"), Line: 1},
1860 },
1861 },
1862 },
1863 },
1864 },
1865 },
1866 {
1867 input: []byte("- record:\n expr:\n"),
1868 output: parser.File{
1869 IsRelaxed: true,
1870 Groups: []parser.Group{
1871 {
1872 Rules: []parser.Rule{
1873 {
1874 Lines: diags.LineRange{First: 1, Last: 2},
1875 Error: parser.ParseError{Err: errors.New("record value cannot be empty"), Line: 1},
1876 },
1877 },
1878 },
1879 },
1880 },
1881 },
1882 {
1883 input: []byte("- record: foo\n expr:\n"),
1884 output: parser.File{
1885 IsRelaxed: true,
1886 Groups: []parser.Group{
1887 {
1888 Rules: []parser.Rule{
1889 {
1890 Lines: diags.LineRange{First: 1, Last: 2},
1891 Error: parser.ParseError{Err: errors.New("expr value cannot be empty"), Line: 2},
1892 },
1893 },
1894 },
1895 },
1896 },
1897 },
1898 {
1899 input: []byte("- record: foo\n"),
1900 output: parser.File{
1901 IsRelaxed: true,
1902 Groups: []parser.Group{
1903 {
1904 Rules: []parser.Rule{
1905 {
1906 Lines: diags.LineRange{First: 1, Last: 1},
1907 Error: parser.ParseError{Err: errors.New("missing expr key"), Line: 1},
1908 },
1909 },
1910 },
1911 },
1912 },
1913 },
1914 {
1915 input: []byte(string(`
1916# pint file/owner bob
1917# pint ignore/begin
1918# pint ignore/end
1919# pint disable up
1920
1921- record: foo
1922 expr: up
1923
1924# pint file/owner alice
1925
1926- record: foo
1927 expr: up
1928
1929# pint ignore/next-line
1930`)),
1931 output: parser.File{
1932 IsRelaxed: true,
1933 Comments: []comments.Comment{
1934 {
1935 Type: comments.FileOwnerType,
1936 Value: comments.Owner{
1937 Name: "bob",
1938 Position: comments.Position{
1939 Offset: 18,
1940 Pos: diags.PositionRanges{
1941 {
1942 Line: 2,
1943 FirstColumn: 1,
1944 LastColumn: 21,
1945 },
1946 },
1947 },
1948 },
1949 },
1950 {
1951 Type: comments.FileOwnerType,
1952 Value: comments.Owner{
1953 Name: "alice",
1954 Position: comments.Position{
1955 Offset: 18,
1956 Pos: diags.PositionRanges{
1957 {
1958 Line: 10,
1959 FirstColumn: 1,
1960 LastColumn: 23,
1961 },
1962 },
1963 },
1964 },
1965 },
1966 },
1967 Groups: []parser.Group{
1968 {
1969 Rules: []parser.Rule{
1970 {
1971 Lines: diags.LineRange{First: 7, Last: 8},
1972 RecordingRule: &parser.RecordingRule{
1973 Record: parser.YamlNode{
1974 Value: "foo",
1975 Pos: diags.PositionRanges{
1976 {Line: 7, FirstColumn: 11, LastColumn: 13},
1977 },
1978 },
1979 Expr: parser.PromQLExpr{
1980 Value: &parser.YamlNode{
1981 Value: "up",
1982 Pos: diags.PositionRanges{
1983 {Line: 8, FirstColumn: 9, LastColumn: 10},
1984 },
1985 },
1986 },
1987 },
1988 },
1989 {
1990 Lines: diags.LineRange{First: 12, Last: 13},
1991 RecordingRule: &parser.RecordingRule{
1992 Record: parser.YamlNode{
1993 Value: "foo",
1994 Pos: diags.PositionRanges{
1995 {Line: 12, FirstColumn: 11, LastColumn: 13},
1996 },
1997 },
1998 Expr: parser.PromQLExpr{
1999 Value: &parser.YamlNode{
2000 Value: "up",
2001 Pos: diags.PositionRanges{
2002 {Line: 13, FirstColumn: 9, LastColumn: 10},
2003 },
2004 },
2005 },
2006 },
2007 },
2008 },
2009 },
2010 },
2011 },
2012 },
2013 {
2014 input: []byte(string(`
2015- alert: Template
2016 expr: &expr up == 0
2017 labels:
2018 notify: &maybe_escalate_notify chat-alerts
2019- alert: Service Down
2020 expr: *expr
2021 labels:
2022 notify: *maybe_escalate_notify
2023 summary: foo
2024`)),
2025 output: parser.File{
2026 IsRelaxed: true,
2027 Groups: []parser.Group{
2028 {
2029 Rules: []parser.Rule{
2030 {
2031 Lines: diags.LineRange{First: 2, Last: 5},
2032 AlertingRule: &parser.AlertingRule{
2033 Alert: parser.YamlNode{
2034 Value: "Template",
2035 Pos: diags.PositionRanges{
2036 {Line: 2, FirstColumn: 10, LastColumn: 17},
2037 },
2038 },
2039 Expr: parser.PromQLExpr{
2040 Value: &parser.YamlNode{
2041 Value: "up == 0",
2042 Pos: diags.PositionRanges{
2043 {Line: 3, FirstColumn: 15, LastColumn: 21},
2044 },
2045 },
2046 },
2047 Labels: &parser.YamlMap{
2048 Key: &parser.YamlNode{
2049 Value: "labels",
2050 Pos: diags.PositionRanges{
2051 {Line: 4, FirstColumn: 3, LastColumn: 8},
2052 },
2053 },
2054 Items: []*parser.YamlKeyValue{
2055 {
2056 Key: &parser.YamlNode{
2057 Value: "notify",
2058 Pos: diags.PositionRanges{
2059 {Line: 5, FirstColumn: 5, LastColumn: 10},
2060 },
2061 },
2062 Value: &parser.YamlNode{
2063 Value: "chat-alerts",
2064 Pos: diags.PositionRanges{
2065 {Line: 5, FirstColumn: 22, LastColumn: 22},
2066 {Line: 5, FirstColumn: 37, LastColumn: 46},
2067 },
2068 },
2069 },
2070 },
2071 },
2072 },
2073 },
2074 {
2075 Lines: diags.LineRange{First: 6, Last: 10},
2076 AlertingRule: &parser.AlertingRule{
2077 Alert: parser.YamlNode{
2078 Value: "Service Down",
2079 Pos: diags.PositionRanges{
2080 {Line: 6, FirstColumn: 10, LastColumn: 21},
2081 },
2082 },
2083 Expr: parser.PromQLExpr{
2084 Value: &parser.YamlNode{
2085 Value: "up == 0",
2086 Pos: diags.PositionRanges{
2087 {Line: 7, FirstColumn: 10, LastColumn: 13}, // points at anchor
2088 },
2089 },
2090 },
2091 Labels: &parser.YamlMap{
2092 Key: &parser.YamlNode{
2093 Value: "labels",
2094 Pos: diags.PositionRanges{
2095 {Line: 8, FirstColumn: 3, LastColumn: 8},
2096 },
2097 },
2098 Items: []*parser.YamlKeyValue{
2099 {
2100 Key: &parser.YamlNode{
2101 Value: "notify",
2102 Pos: diags.PositionRanges{
2103 {Line: 9, FirstColumn: 5, LastColumn: 10},
2104 },
2105 },
2106 Value: &parser.YamlNode{
2107 Value: "chat-alerts",
2108 Pos: diags.PositionRanges{
2109 {Line: 9, FirstColumn: 14, LastColumn: 34},
2110 },
2111 },
2112 },
2113 {
2114 Key: &parser.YamlNode{
2115 Value: "summary",
2116 Pos: diags.PositionRanges{
2117 {Line: 10, FirstColumn: 5, LastColumn: 11},
2118 },
2119 },
2120 Value: &parser.YamlNode{
2121 Value: "foo",
2122 Pos: diags.PositionRanges{
2123 {Line: 10, FirstColumn: 14, LastColumn: 16},
2124 },
2125 },
2126 },
2127 },
2128 },
2129 },
2130 },
2131 },
2132 },
2133 },
2134 },
2135 },
2136 {
2137 // YAML alias on record value is resolved correctly
2138 input: []byte(`
2139- record: &name foo
2140 expr: expr1
2141- record: *name
2142 expr: expr2
2143`),
2144 output: parser.File{
2145 IsRelaxed: true,
2146 Groups: []parser.Group{
2147 {
2148 Rules: []parser.Rule{
2149 {
2150 Lines: diags.LineRange{First: 2, Last: 3},
2151 RecordingRule: &parser.RecordingRule{
2152 Record: parser.YamlNode{
2153 Value: "foo",
2154 Pos: diags.PositionRanges{
2155 {Line: 2, FirstColumn: 17, LastColumn: 19},
2156 },
2157 },
2158 Expr: parser.PromQLExpr{
2159 Value: &parser.YamlNode{
2160 Value: "expr1",
2161 Pos: diags.PositionRanges{
2162 {Line: 3, FirstColumn: 9, LastColumn: 13},
2163 },
2164 },
2165 },
2166 },
2167 },
2168 {
2169 Lines: diags.LineRange{First: 4, Last: 5},
2170 RecordingRule: &parser.RecordingRule{
2171 Record: parser.YamlNode{
2172 Value: "foo",
2173 Pos: diags.PositionRanges{
2174 {Line: 4, FirstColumn: 12, LastColumn: 15},
2175 },
2176 },
2177 Expr: parser.PromQLExpr{
2178 Value: &parser.YamlNode{
2179 Value: "expr2",
2180 Pos: diags.PositionRanges{
2181 {Line: 5, FirstColumn: 9, LastColumn: 13},
2182 },
2183 },
2184 },
2185 },
2186 },
2187 },
2188 },
2189 },
2190 },
2191 },
2192 {
2193 // YAML alias on alert value is resolved correctly
2194 input: []byte(`
2195- alert: &aname MyAlert
2196 expr: up == 0
2197- alert: *aname
2198 expr: up == 1
2199`),
2200 output: parser.File{
2201 IsRelaxed: true,
2202 Groups: []parser.Group{
2203 {
2204 Rules: []parser.Rule{
2205 {
2206 Lines: diags.LineRange{First: 2, Last: 3},
2207 AlertingRule: &parser.AlertingRule{
2208 Alert: parser.YamlNode{
2209 Value: "MyAlert",
2210 Pos: diags.PositionRanges{
2211 {Line: 2, FirstColumn: 17, LastColumn: 23},
2212 },
2213 },
2214 Expr: parser.PromQLExpr{
2215 Value: &parser.YamlNode{
2216 Value: "up == 0",
2217 Pos: diags.PositionRanges{
2218 {Line: 3, FirstColumn: 9, LastColumn: 15},
2219 },
2220 },
2221 },
2222 },
2223 },
2224 {
2225 Lines: diags.LineRange{First: 4, Last: 5},
2226 AlertingRule: &parser.AlertingRule{
2227 Alert: parser.YamlNode{
2228 Value: "MyAlert",
2229 Pos: diags.PositionRanges{
2230 {Line: 4, FirstColumn: 11, LastColumn: 15},
2231 },
2232 },
2233 Expr: parser.PromQLExpr{
2234 Value: &parser.YamlNode{
2235 Value: "up == 1",
2236 Pos: diags.PositionRanges{
2237 {Line: 5, FirstColumn: 9, LastColumn: 15},
2238 },
2239 },
2240 },
2241 },
2242 },
2243 },
2244 },
2245 },
2246 },
2247 },
2248 {
2249 // YAML alias on for duration is resolved correctly
2250 input: []byte(`
2251- alert: alert1
2252 expr: up == 0
2253 for: &dur 5m
2254- alert: alert2
2255 expr: up == 1
2256 for: *dur
2257`),
2258 output: parser.File{
2259 IsRelaxed: true,
2260 Groups: []parser.Group{
2261 {
2262 Rules: []parser.Rule{
2263 {
2264 Lines: diags.LineRange{First: 2, Last: 4},
2265 AlertingRule: &parser.AlertingRule{
2266 Alert: parser.YamlNode{
2267 Value: "alert1",
2268 Pos: diags.PositionRanges{
2269 {Line: 2, FirstColumn: 10, LastColumn: 15},
2270 },
2271 },
2272 Expr: parser.PromQLExpr{
2273 Value: &parser.YamlNode{
2274 Value: "up == 0",
2275 Pos: diags.PositionRanges{
2276 {Line: 3, FirstColumn: 9, LastColumn: 15},
2277 },
2278 },
2279 },
2280 For: &parser.YamlDuration{
2281 Raw: "5m",
2282 Value: 5 * time.Minute,
2283 Pos: diags.PositionRanges{
2284 {Line: 4, FirstColumn: 13, LastColumn: 14},
2285 },
2286 },
2287 },
2288 },
2289 {
2290 Lines: diags.LineRange{First: 5, Last: 7},
2291 AlertingRule: &parser.AlertingRule{
2292 Alert: parser.YamlNode{
2293 Value: "alert2",
2294 Pos: diags.PositionRanges{
2295 {Line: 5, FirstColumn: 10, LastColumn: 15},
2296 },
2297 },
2298 Expr: parser.PromQLExpr{
2299 Value: &parser.YamlNode{
2300 Value: "up == 1",
2301 Pos: diags.PositionRanges{
2302 {Line: 6, FirstColumn: 9, LastColumn: 15},
2303 },
2304 },
2305 },
2306 For: &parser.YamlDuration{
2307 Raw: "5m",
2308 Value: 5 * time.Minute,
2309 Pos: diags.PositionRanges{
2310 {Line: 7, FirstColumn: 9, LastColumn: 11},
2311 },
2312 },
2313 },
2314 },
2315 },
2316 },
2317 },
2318 },
2319 },
2320 {
2321 // YAML alias on keep_firing_for duration is resolved correctly
2322 input: []byte(`
2323- alert: alert1
2324 expr: up == 0
2325 keep_firing_for: &kff 10m
2326- alert: alert2
2327 expr: up == 1
2328 keep_firing_for: *kff
2329`),
2330 output: parser.File{
2331 IsRelaxed: true,
2332 Groups: []parser.Group{
2333 {
2334 Rules: []parser.Rule{
2335 {
2336 Lines: diags.LineRange{First: 2, Last: 4},
2337 AlertingRule: &parser.AlertingRule{
2338 Alert: parser.YamlNode{
2339 Value: "alert1",
2340 Pos: diags.PositionRanges{
2341 {Line: 2, FirstColumn: 10, LastColumn: 15},
2342 },
2343 },
2344 Expr: parser.PromQLExpr{
2345 Value: &parser.YamlNode{
2346 Value: "up == 0",
2347 Pos: diags.PositionRanges{
2348 {Line: 3, FirstColumn: 9, LastColumn: 15},
2349 },
2350 },
2351 },
2352 KeepFiringFor: &parser.YamlDuration{
2353 Raw: "10m",
2354 Value: 10 * time.Minute,
2355 Pos: diags.PositionRanges{
2356 {Line: 4, FirstColumn: 25, LastColumn: 27},
2357 },
2358 },
2359 },
2360 },
2361 {
2362 Lines: diags.LineRange{First: 5, Last: 7},
2363 AlertingRule: &parser.AlertingRule{
2364 Alert: parser.YamlNode{
2365 Value: "alert2",
2366 Pos: diags.PositionRanges{
2367 {Line: 5, FirstColumn: 10, LastColumn: 15},
2368 },
2369 },
2370 Expr: parser.PromQLExpr{
2371 Value: &parser.YamlNode{
2372 Value: "up == 1",
2373 Pos: diags.PositionRanges{
2374 {Line: 6, FirstColumn: 9, LastColumn: 15},
2375 },
2376 },
2377 },
2378 KeepFiringFor: &parser.YamlDuration{
2379 Raw: "10m",
2380 Value: 10 * time.Minute,
2381 Pos: diags.PositionRanges{
2382 {Line: 7, FirstColumn: 21, LastColumn: 23},
2383 },
2384 },
2385 },
2386 },
2387 },
2388 },
2389 },
2390 },
2391 },
2392 {
2393 // YAML alias on annotations map is resolved correctly
2394 input: []byte(`
2395- alert: alert1
2396 expr: up == 0
2397 annotations: &ann
2398 summary: foo is down
2399- alert: alert2
2400 expr: up == 1
2401 annotations: *ann
2402`),
2403 output: parser.File{
2404 IsRelaxed: true,
2405 Groups: []parser.Group{
2406 {
2407 Rules: []parser.Rule{
2408 {
2409 Lines: diags.LineRange{First: 2, Last: 5},
2410 AlertingRule: &parser.AlertingRule{
2411 Alert: parser.YamlNode{
2412 Value: "alert1",
2413 Pos: diags.PositionRanges{
2414 {Line: 2, FirstColumn: 10, LastColumn: 15},
2415 },
2416 },
2417 Expr: parser.PromQLExpr{
2418 Value: &parser.YamlNode{
2419 Value: "up == 0",
2420 Pos: diags.PositionRanges{
2421 {Line: 3, FirstColumn: 9, LastColumn: 15},
2422 },
2423 },
2424 },
2425 Annotations: &parser.YamlMap{
2426 Key: &parser.YamlNode{
2427 Value: "annotations",
2428 Pos: diags.PositionRanges{
2429 {Line: 4, FirstColumn: 3, LastColumn: 13},
2430 },
2431 },
2432 Items: []*parser.YamlKeyValue{
2433 {
2434 Key: &parser.YamlNode{
2435 Value: "summary",
2436 Pos: diags.PositionRanges{
2437 {Line: 5, FirstColumn: 5, LastColumn: 11},
2438 },
2439 },
2440 Value: &parser.YamlNode{
2441 Value: "foo is down",
2442 Pos: diags.PositionRanges{
2443 {Line: 5, FirstColumn: 14, LastColumn: 24},
2444 },
2445 },
2446 },
2447 },
2448 },
2449 },
2450 },
2451 {
2452 Lines: diags.LineRange{First: 6, Last: 8},
2453 AlertingRule: &parser.AlertingRule{
2454 Alert: parser.YamlNode{
2455 Value: "alert2",
2456 Pos: diags.PositionRanges{
2457 {Line: 6, FirstColumn: 10, LastColumn: 15},
2458 },
2459 },
2460 Expr: parser.PromQLExpr{
2461 Value: &parser.YamlNode{
2462 Value: "up == 1",
2463 Pos: diags.PositionRanges{
2464 {Line: 7, FirstColumn: 9, LastColumn: 15},
2465 },
2466 },
2467 },
2468 Annotations: &parser.YamlMap{
2469 Key: &parser.YamlNode{
2470 Value: "annotations",
2471 Pos: diags.PositionRanges{
2472 {Line: 8, FirstColumn: 3, LastColumn: 13},
2473 },
2474 },
2475 Items: []*parser.YamlKeyValue{
2476 {
2477 Key: &parser.YamlNode{
2478 Value: "summary",
2479 Pos: diags.PositionRanges{
2480 {Line: 5, FirstColumn: 5, LastColumn: 11},
2481 },
2482 },
2483 Value: &parser.YamlNode{
2484 Value: "foo is down",
2485 Pos: diags.PositionRanges{
2486 {Line: 5, FirstColumn: 14, LastColumn: 24},
2487 },
2488 },
2489 },
2490 },
2491 },
2492 },
2493 },
2494 },
2495 },
2496 },
2497 },
2498 },
2499 {
2500 // Merge key merges default alert options into alert rules
2501 input: []byte(`
2502- &defaults
2503 for: 5m
2504 labels:
2505 severity: warning
2506- alert: foo
2507 expr: up == 0
2508 <<: *defaults
2509`),
2510 output: parser.File{
2511 IsRelaxed: true,
2512 Groups: []parser.Group{
2513 {
2514 Rules: []parser.Rule{
2515 {
2516 Lines: diags.LineRange{First: 3, Last: 7},
2517 AlertingRule: &parser.AlertingRule{
2518 Alert: parser.YamlNode{
2519 Value: "foo",
2520 Pos: diags.PositionRanges{
2521 {Line: 6, FirstColumn: 10, LastColumn: 12},
2522 },
2523 },
2524 Expr: parser.PromQLExpr{
2525 Value: &parser.YamlNode{
2526 Value: "up == 0",
2527 Pos: diags.PositionRanges{
2528 {Line: 7, FirstColumn: 9, LastColumn: 15},
2529 },
2530 },
2531 },
2532 For: &parser.YamlDuration{
2533 Raw: "5m",
2534 Value: 5 * time.Minute,
2535 Pos: diags.PositionRanges{
2536 {Line: 3, FirstColumn: 8, LastColumn: 9},
2537 },
2538 },
2539 Labels: &parser.YamlMap{
2540 Key: &parser.YamlNode{
2541 Value: "labels",
2542 Pos: diags.PositionRanges{
2543 {Line: 4, FirstColumn: 3, LastColumn: 8},
2544 },
2545 },
2546 Items: []*parser.YamlKeyValue{
2547 {
2548 Key: &parser.YamlNode{
2549 Value: "severity",
2550 Pos: diags.PositionRanges{
2551 {Line: 5, FirstColumn: 5, LastColumn: 12},
2552 },
2553 },
2554 Value: &parser.YamlNode{
2555 Value: "warning",
2556 Pos: diags.PositionRanges{
2557 {Line: 5, FirstColumn: 15, LastColumn: 21},
2558 },
2559 },
2560 },
2561 },
2562 },
2563 },
2564 },
2565 },
2566 },
2567 },
2568 },
2569 },
2570 {
2571 // Merge key with local override - local for value takes precedence over merged for
2572 input: []byte(`
2573- &defaults
2574 for: 5m
2575 labels:
2576 severity: warning
2577- alert: foo
2578 expr: up == 0
2579 for: 10m
2580 <<: *defaults
2581`),
2582 output: parser.File{
2583 IsRelaxed: true,
2584 Groups: []parser.Group{
2585 {
2586 Rules: []parser.Rule{
2587 {
2588 Lines: diags.LineRange{First: 4, Last: 8},
2589 AlertingRule: &parser.AlertingRule{
2590 Alert: parser.YamlNode{
2591 Value: "foo",
2592 Pos: diags.PositionRanges{
2593 {Line: 6, FirstColumn: 10, LastColumn: 12},
2594 },
2595 },
2596 Expr: parser.PromQLExpr{
2597 Value: &parser.YamlNode{
2598 Value: "up == 0",
2599 Pos: diags.PositionRanges{
2600 {Line: 7, FirstColumn: 9, LastColumn: 15},
2601 },
2602 },
2603 },
2604 For: &parser.YamlDuration{
2605 Raw: "10m",
2606 Value: 10 * time.Minute,
2607 Pos: diags.PositionRanges{
2608 {Line: 8, FirstColumn: 8, LastColumn: 10},
2609 },
2610 },
2611 Labels: &parser.YamlMap{
2612 Key: &parser.YamlNode{
2613 Value: "labels",
2614 Pos: diags.PositionRanges{
2615 {Line: 4, FirstColumn: 3, LastColumn: 8},
2616 },
2617 },
2618 Items: []*parser.YamlKeyValue{
2619 {
2620 Key: &parser.YamlNode{
2621 Value: "severity",
2622 Pos: diags.PositionRanges{
2623 {Line: 5, FirstColumn: 5, LastColumn: 12},
2624 },
2625 },
2626 Value: &parser.YamlNode{
2627 Value: "warning",
2628 Pos: diags.PositionRanges{
2629 {Line: 5, FirstColumn: 15, LastColumn: 21},
2630 },
2631 },
2632 },
2633 },
2634 },
2635 },
2636 },
2637 },
2638 },
2639 },
2640 },
2641 },
2642 {
2643 // Sequence merge key <<: [*a, *b] merges defaults from both aliases
2644 input: []byte(`
2645- &defaults_a
2646 for: 5m
2647- &defaults_b
2648 labels:
2649 severity: warning
2650- alert: foo
2651 expr: up == 0
2652 <<: [*defaults_a, *defaults_b]
2653`),
2654 output: parser.File{
2655 IsRelaxed: true,
2656 Groups: []parser.Group{
2657 {
2658 Rules: []parser.Rule{
2659 {
2660 Lines: diags.LineRange{First: 3, Last: 8},
2661 AlertingRule: &parser.AlertingRule{
2662 Alert: parser.YamlNode{
2663 Value: "foo",
2664 Pos: diags.PositionRanges{
2665 {Line: 7, FirstColumn: 10, LastColumn: 12},
2666 },
2667 },
2668 Expr: parser.PromQLExpr{
2669 Value: &parser.YamlNode{
2670 Value: "up == 0",
2671 Pos: diags.PositionRanges{
2672 {Line: 8, FirstColumn: 9, LastColumn: 15},
2673 },
2674 },
2675 },
2676 For: &parser.YamlDuration{
2677 Raw: "5m",
2678 Value: 5 * time.Minute,
2679 Pos: diags.PositionRanges{
2680 {Line: 3, FirstColumn: 8, LastColumn: 9},
2681 },
2682 },
2683 Labels: &parser.YamlMap{
2684 Key: &parser.YamlNode{
2685 Value: "labels",
2686 Pos: diags.PositionRanges{
2687 {Line: 5, FirstColumn: 3, LastColumn: 8},
2688 },
2689 },
2690 Items: []*parser.YamlKeyValue{
2691 {
2692 Key: &parser.YamlNode{
2693 Value: "severity",
2694 Pos: diags.PositionRanges{
2695 {Line: 6, FirstColumn: 5, LastColumn: 12},
2696 },
2697 },
2698 Value: &parser.YamlNode{
2699 Value: "warning",
2700 Pos: diags.PositionRanges{
2701 {Line: 6, FirstColumn: 15, LastColumn: 21},
2702 },
2703 },
2704 },
2705 },
2706 },
2707 },
2708 },
2709 },
2710 },
2711 },
2712 },
2713 },
2714 {
2715 input: []byte(`
2716- record: invalid metric name
2717 expr: bar
2718`),
2719 output: parser.File{
2720 IsRelaxed: true,
2721 Groups: []parser.Group{
2722 {
2723 Rules: []parser.Rule{
2724 {
2725 Lines: diags.LineRange{First: 2, Last: 3},
2726 Error: parser.ParseError{Err: errors.New("invalid recording rule name: invalid metric name"), Line: 2},
2727 },
2728 },
2729 },
2730 },
2731 },
2732 },
2733 {
2734 input: []byte(`
2735- record: utf-8 enabled name
2736 expr: bar
2737 labels:
2738 "a b c": bar
2739`),
2740 names: model.UTF8Validation,
2741 output: parser.File{
2742 IsRelaxed: true,
2743 Groups: []parser.Group{
2744 {
2745 Rules: []parser.Rule{
2746 {
2747 Lines: diags.LineRange{First: 2, Last: 5},
2748 RecordingRule: &parser.RecordingRule{
2749 Record: parser.YamlNode{
2750 Value: "utf-8 enabled name",
2751 Pos: diags.PositionRanges{
2752 {Line: 2, FirstColumn: 11, LastColumn: 28},
2753 },
2754 },
2755 Expr: parser.PromQLExpr{
2756 Value: &parser.YamlNode{
2757 Value: "bar",
2758 Pos: diags.PositionRanges{
2759 {Line: 3, FirstColumn: 9, LastColumn: 11},
2760 },
2761 },
2762 },
2763 Labels: &parser.YamlMap{
2764 Key: &parser.YamlNode{
2765 Value: "labels",
2766 Pos: diags.PositionRanges{
2767 {Line: 4, FirstColumn: 3, LastColumn: 8},
2768 },
2769 },
2770 Items: []*parser.YamlKeyValue{
2771 {
2772 Key: &parser.YamlNode{
2773 Value: "a b c",
2774 Pos: diags.PositionRanges{
2775 {Line: 5, FirstColumn: 6, LastColumn: 10},
2776 },
2777 },
2778 Value: &parser.YamlNode{
2779 Value: "bar",
2780 Pos: diags.PositionRanges{
2781 {Line: 5, FirstColumn: 14, LastColumn: 16},
2782 },
2783 },
2784 },
2785 },
2786 },
2787 },
2788 },
2789 },
2790 },
2791 },
2792 },
2793 },
2794 {
2795 input: []byte(`
2796- record: foo
2797 expr: bar
2798 labels:
2799 "foo bar": yes
2800`),
2801 output: parser.File{
2802 IsRelaxed: true,
2803 Groups: []parser.Group{
2804 {
2805 Rules: []parser.Rule{
2806 {
2807 Lines: diags.LineRange{First: 2, Last: 5},
2808 Error: parser.ParseError{Err: errors.New("invalid label name: foo bar"), Line: 5},
2809 },
2810 },
2811 },
2812 },
2813 },
2814 },
2815 {
2816 input: []byte(`
2817- alert: foo
2818 expr: bar
2819 labels:
2820 "foo bar": yes
2821`),
2822 output: parser.File{
2823 IsRelaxed: true,
2824 Groups: []parser.Group{
2825 {
2826 Rules: []parser.Rule{
2827 {
2828 Lines: diags.LineRange{First: 2, Last: 5},
2829 Error: parser.ParseError{Err: errors.New("invalid label name: foo bar"), Line: 5},
2830 },
2831 },
2832 },
2833 },
2834 },
2835 },
2836 {
2837 input: []byte(`
2838- alert: foo
2839 expr: bar
2840 labels:
2841 "{{ $value }}": yes
2842`),
2843 output: parser.File{
2844 IsRelaxed: true,
2845 Groups: []parser.Group{
2846 {
2847 Rules: []parser.Rule{
2848 {
2849 Lines: diags.LineRange{First: 2, Last: 5},
2850 Error: parser.ParseError{Err: errors.New("invalid label name: {{ $value }}"), Line: 5},
2851 },
2852 },
2853 },
2854 },
2855 },
2856 },
2857 {
2858 input: []byte(`
2859- alert: foo
2860 expr: bar
2861 annotations:
2862 "foo bar": yes
2863`),
2864 output: parser.File{
2865 IsRelaxed: true,
2866 Groups: []parser.Group{
2867 {
2868 Rules: []parser.Rule{
2869 {
2870 Lines: diags.LineRange{First: 2, Last: 5},
2871 Error: parser.ParseError{Err: errors.New("invalid annotation name: foo bar"), Line: 5},
2872 },
2873 },
2874 },
2875 },
2876 },
2877 },
2878 {
2879 input: []byte(`
2880- alert: foo
2881 expr: bar
2882 labels:
2883 foo: ` + string("\xed\xbf\xbf")),
2884 // Label values are invalid only if they aren't valid UTF-8 strings
2885 // which also makes them unparsable by YAML.
2886 output: parser.File{
2887 IsRelaxed: true,
2888 Error: parser.ParseError{
2889 Err: errors.New("yaml: invalid Unicode character"),
2890 Line: 1,
2891 },
2892 },
2893 },
2894 {
2895 input: []byte(`
2896- alert: foo
2897 expr: bar
2898 annotations:
2899 "{{ $value }}": yes
2900`),
2901 output: parser.File{
2902 IsRelaxed: true,
2903 Groups: []parser.Group{
2904 {
2905 Rules: []parser.Rule{
2906 {
2907 Lines: diags.LineRange{First: 2, Last: 5},
2908 Error: parser.ParseError{Err: errors.New("invalid annotation name: {{ $value }}"), Line: 5},
2909 },
2910 },
2911 },
2912 },
2913 },
2914 },
2915 {
2916 input: []byte(`
2917- record: foo
2918 expr: bar
2919 keep_firing_for: 5m
2920`),
2921 output: parser.File{
2922 IsRelaxed: true,
2923 Groups: []parser.Group{
2924 {
2925 Rules: []parser.Rule{
2926 {
2927 Lines: diags.LineRange{First: 2, Last: 4},
2928 Error: parser.ParseError{Err: errors.New("invalid field 'keep_firing_for' in recording rule"), Line: 4},
2929 },
2930 },
2931 },
2932 },
2933 },
2934 },
2935 {
2936 input: []byte(`
2937- record: foo
2938 expr: bar
2939 for: 5m
2940`),
2941 output: parser.File{
2942 IsRelaxed: true,
2943 Groups: []parser.Group{
2944 {
2945 Rules: []parser.Rule{
2946 {
2947 Lines: diags.LineRange{First: 2, Last: 4},
2948 Error: parser.ParseError{Err: errors.New("invalid field 'for' in recording rule"), Line: 4},
2949 },
2950 },
2951 },
2952 },
2953 },
2954 },
2955 {
2956 input: []byte(`
2957- record: foo
2958 expr: bar
2959 annotations:
2960 foo: bar
2961`),
2962 output: parser.File{
2963 IsRelaxed: true,
2964 Groups: []parser.Group{
2965 {
2966 Rules: []parser.Rule{
2967 {
2968 Lines: diags.LineRange{First: 2, Last: 5},
2969 Error: parser.ParseError{Err: errors.New("invalid field 'annotations' in recording rule"), Line: 4},
2970 },
2971 },
2972 },
2973 },
2974 },
2975 },
2976 // Tag tests
2977 {
2978 input: []byte(`
2979- record: 5
2980 expr: bar
2981`),
2982 output: parser.File{
2983 IsRelaxed: true,
2984 Groups: []parser.Group{
2985 {
2986 Rules: []parser.Rule{
2987 {
2988 Lines: diags.LineRange{First: 2, Last: 3},
2989 Error: parser.ParseError{Err: errors.New("record value must be a string, got integer instead"), Line: 2},
2990 },
2991 },
2992 },
2993 },
2994 },
2995 },
2996 {
2997 input: []byte(`
2998- alert: 5
2999 expr: bar
3000`),
3001 output: parser.File{
3002 IsRelaxed: true,
3003 Groups: []parser.Group{
3004 {
3005 Rules: []parser.Rule{
3006 {
3007 Lines: diags.LineRange{First: 2, Last: 3},
3008 Error: parser.ParseError{Err: errors.New("alert value must be a string, got integer instead"), Line: 2},
3009 },
3010 },
3011 },
3012 },
3013 },
3014 },
3015 {
3016 input: []byte(`
3017- record: foo
3018 expr: 5
3019`),
3020 output: parser.File{
3021 IsRelaxed: true,
3022 Groups: []parser.Group{
3023 {
3024 Rules: []parser.Rule{
3025 {
3026 Lines: diags.LineRange{First: 2, Last: 3},
3027 Error: parser.ParseError{Err: errors.New("expr value must be a string, got integer instead"), Line: 3},
3028 },
3029 },
3030 },
3031 },
3032 },
3033 },
3034 {
3035 input: []byte(`
3036- alert: foo
3037 expr: bar
3038 for: 5
3039`),
3040 output: parser.File{
3041 IsRelaxed: true,
3042 Groups: []parser.Group{
3043 {
3044 Rules: []parser.Rule{
3045 {
3046 Lines: diags.LineRange{First: 2, Last: 4},
3047 Error: parser.ParseError{Err: errors.New("for value must be a string, got integer instead"), Line: 4},
3048 },
3049 },
3050 },
3051 },
3052 },
3053 },
3054 {
3055 input: []byte(`
3056- alert: foo
3057 expr: bar
3058 keep_firing_for: 5
3059`),
3060 output: parser.File{
3061 IsRelaxed: true,
3062 Groups: []parser.Group{
3063 {
3064 Rules: []parser.Rule{
3065 {
3066 Lines: diags.LineRange{First: 2, Last: 4},
3067 Error: parser.ParseError{Err: errors.New("keep_firing_for value must be a string, got integer instead"), Line: 4},
3068 },
3069 },
3070 },
3071 },
3072 },
3073 },
3074 {
3075 input: []byte(`
3076- record: foo
3077 expr: bar
3078 labels: []
3079`),
3080 output: parser.File{
3081 IsRelaxed: true,
3082 Groups: []parser.Group{
3083 {
3084 Rules: []parser.Rule{
3085 {
3086 Lines: diags.LineRange{First: 2, Last: 4},
3087 Error: parser.ParseError{Err: errors.New("labels value must be a mapping, got list instead"), Line: 4},
3088 },
3089 },
3090 },
3091 },
3092 },
3093 },
3094 {
3095 input: []byte(`
3096- alert: foo
3097 expr: bar
3098 labels: {}
3099 annotations: []
3100`),
3101 output: parser.File{
3102 IsRelaxed: true,
3103 Groups: []parser.Group{
3104 {
3105 Rules: []parser.Rule{
3106 {
3107 Lines: diags.LineRange{First: 2, Last: 5},
3108 Error: parser.ParseError{Err: errors.New("annotations value must be a mapping, got list instead"), Line: 5},
3109 },
3110 },
3111 },
3112 },
3113 },
3114 },
3115 {
3116 input: []byte(`
3117- alert: foo
3118 expr: bar
3119 labels:
3120 foo: 3
3121 annotations:
3122 bar: "5"
3123`),
3124 output: parser.File{
3125 IsRelaxed: true,
3126 Groups: []parser.Group{
3127 {
3128 Rules: []parser.Rule{
3129 {
3130 Lines: diags.LineRange{First: 2, Last: 7},
3131 Error: parser.ParseError{Err: errors.New("labels foo value must be a string, got integer instead"), Line: 5},
3132 },
3133 },
3134 },
3135 },
3136 },
3137 },
3138 {
3139 input: []byte(`
3140- alert: foo
3141 expr: bar
3142 labels: {}
3143 annotations:
3144 foo: "3"
3145 bar: 5
3146`),
3147 output: parser.File{
3148 IsRelaxed: true,
3149 Groups: []parser.Group{
3150 {
3151 Rules: []parser.Rule{
3152 {
3153 Lines: diags.LineRange{First: 2, Last: 7},
3154 Error: parser.ParseError{Err: errors.New("annotations bar value must be a string, got integer instead"), Line: 7},
3155 },
3156 },
3157 },
3158 },
3159 },
3160 },
3161 {
3162 input: []byte(`
3163- record: foo
3164 expr: bar
3165 labels: 4
3166`),
3167 output: parser.File{
3168 IsRelaxed: true,
3169 Groups: []parser.Group{
3170 {
3171 Rules: []parser.Rule{
3172 {
3173 Lines: diags.LineRange{First: 2, Last: 4},
3174 Error: parser.ParseError{Err: errors.New("labels value must be a mapping, got integer instead"), Line: 4},
3175 },
3176 },
3177 },
3178 },
3179 },
3180 },
3181 {
3182 input: []byte(`
3183- record: foo
3184 expr: bar
3185 labels: true
3186`),
3187 output: parser.File{
3188 IsRelaxed: true,
3189 Groups: []parser.Group{
3190 {
3191 Rules: []parser.Rule{
3192 {
3193 Lines: diags.LineRange{First: 2, Last: 4},
3194 Error: parser.ParseError{Err: errors.New("labels value must be a mapping, got bool instead"), Line: 4},
3195 },
3196 },
3197 },
3198 },
3199 },
3200 },
3201 {
3202 input: []byte(`
3203- record: foo
3204 expr: bar
3205 labels: null
3206`),
3207 output: parser.File{
3208 IsRelaxed: true,
3209 Groups: []parser.Group{
3210 {
3211 Rules: []parser.Rule{
3212 {
3213 Lines: diags.LineRange{First: 2, Last: 4},
3214 RecordingRule: &parser.RecordingRule{
3215 Record: parser.YamlNode{
3216 Value: "foo",
3217 Pos: diags.PositionRanges{
3218 {Line: 2, FirstColumn: 11, LastColumn: 13},
3219 },
3220 },
3221 Expr: parser.PromQLExpr{
3222 Value: &parser.YamlNode{
3223 Value: "bar",
3224 Pos: diags.PositionRanges{
3225 {Line: 3, FirstColumn: 9, LastColumn: 11},
3226 },
3227 },
3228 },
3229 Labels: &parser.YamlMap{
3230 Key: &parser.YamlNode{
3231 Value: "labels",
3232 Pos: diags.PositionRanges{
3233 {Line: 4, FirstColumn: 3, LastColumn: 8},
3234 },
3235 },
3236 },
3237 },
3238 },
3239 },
3240 },
3241 },
3242 },
3243 },
3244 {
3245 input: []byte(`
3246- record: true
3247 expr: bar
3248`),
3249 output: parser.File{
3250 IsRelaxed: true,
3251 Groups: []parser.Group{
3252 {
3253 Rules: []parser.Rule{
3254 {
3255 Lines: diags.LineRange{First: 2, Last: 3},
3256 Error: parser.ParseError{Err: errors.New("record value must be a string, got bool instead"), Line: 2},
3257 },
3258 },
3259 },
3260 },
3261 },
3262 },
3263 {
3264 input: []byte(`
3265- record:
3266 query: foo
3267 expr: bar
3268`),
3269 output: parser.File{
3270 IsRelaxed: true,
3271 Groups: []parser.Group{
3272 {
3273 Rules: []parser.Rule{
3274 {
3275 Lines: diags.LineRange{First: 2, Last: 4},
3276 Error: parser.ParseError{Err: errors.New("record value must be a string, got mapping instead"), Line: 3},
3277 },
3278 },
3279 },
3280 },
3281 },
3282 },
3283 {
3284 input: []byte(`
3285- record: foo
3286 expr: bar
3287 labels: some
3288`),
3289 output: parser.File{
3290 IsRelaxed: true,
3291 Groups: []parser.Group{
3292 {
3293 Rules: []parser.Rule{
3294 {
3295 Lines: diags.LineRange{First: 2, Last: 4},
3296 Error: parser.ParseError{Err: errors.New("labels value must be a mapping, got string instead"), Line: 4},
3297 },
3298 },
3299 },
3300 },
3301 },
3302 },
3303 {
3304 input: []byte(`
3305- record: foo
3306 expr: bar
3307 labels: !!binary "SGVsbG8sIFdvcmxkIQ=="
3308`),
3309 output: parser.File{
3310 IsRelaxed: true,
3311 Groups: []parser.Group{
3312 {
3313 Rules: []parser.Rule{
3314 {
3315 Lines: diags.LineRange{First: 2, Last: 4},
3316 Error: parser.ParseError{
3317 Err: errors.New("labels value must be a mapping, got binary data instead"),
3318 Line: 4,
3319 },
3320 },
3321 },
3322 },
3323 },
3324 },
3325 },
3326 {
3327 input: []byte(`
3328- alert: foo
3329 expr: bar
3330 for: 1.23
3331`),
3332 output: parser.File{
3333 IsRelaxed: true,
3334 Groups: []parser.Group{
3335 {
3336 Rules: []parser.Rule{
3337 {
3338 Lines: diags.LineRange{First: 2, Last: 4},
3339 Error: parser.ParseError{
3340 Err: errors.New("for value must be a string, got float instead"),
3341 Line: 4,
3342 },
3343 },
3344 },
3345 },
3346 },
3347 },
3348 },
3349 {
3350 input: []byte(`
3351- record: foo
3352 expr: bar
3353 labels: !!garbage "SGVsbG8sIFdvcmxkIQ=="
3354`),
3355 output: parser.File{
3356 IsRelaxed: true,
3357 Groups: []parser.Group{
3358 {
3359 Rules: []parser.Rule{
3360 {
3361 Lines: diags.LineRange{First: 2, Last: 4},
3362 Error: parser.ParseError{Err: errors.New("labels value must be a mapping, got garbage instead"), Line: 4},
3363 },
3364 },
3365 },
3366 },
3367 },
3368 },
3369 {
3370 input: []byte(`
3371- record: foo
3372 expr: bar
3373 labels: !! "SGVsbG8sIFdvcmxkIQ=="
3374`),
3375 output: parser.File{
3376 IsRelaxed: true,
3377 Error: parser.ParseError{
3378 Err: errors.New("did not find expected tag URI"),
3379 Line: 4,
3380 },
3381 },
3382 },
3383 {
3384 input: []byte(`
3385- record: &foo foo
3386 expr: bar
3387 labels: *foo
3388`),
3389 output: parser.File{
3390 IsRelaxed: true,
3391 Groups: []parser.Group{
3392 {
3393 Rules: []parser.Rule{
3394 {
3395 Lines: diags.LineRange{First: 2, Last: 4},
3396 Error: parser.ParseError{
3397 Err: errors.New("labels value must be a mapping, got string instead"),
3398 Line: 4,
3399 },
3400 },
3401 },
3402 },
3403 },
3404 },
3405 },
3406 // Multi-document tests
3407 {
3408 input: []byte(`---
3409- expr: foo
3410 record: foo
3411---
3412- expr: bar
3413`),
3414 output: parser.File{
3415 IsRelaxed: true,
3416 Groups: []parser.Group{
3417 {
3418 Rules: []parser.Rule{
3419 {
3420 Lines: diags.LineRange{First: 2, Last: 3},
3421 RecordingRule: &parser.RecordingRule{
3422 Record: parser.YamlNode{
3423 Value: "foo",
3424 Pos: diags.PositionRanges{
3425 {Line: 3, FirstColumn: 11, LastColumn: 13},
3426 },
3427 },
3428 Expr: parser.PromQLExpr{
3429 Value: &parser.YamlNode{
3430 Value: "foo",
3431 Pos: diags.PositionRanges{
3432 {Line: 2, FirstColumn: 9, LastColumn: 11},
3433 },
3434 },
3435 },
3436 },
3437 },
3438 },
3439 },
3440 {
3441 Rules: []parser.Rule{
3442 {
3443 Lines: diags.LineRange{First: 5, Last: 5},
3444 Error: parser.ParseError{
3445 Err: errors.New("incomplete rule, no alert or record key"),
3446 Line: 5,
3447 },
3448 },
3449 },
3450 },
3451 },
3452 },
3453 },
3454 {
3455 input: []byte(`---
3456- expr: foo
3457 record: foo
3458---
3459- expr: bar
3460 record: bar
3461 expr: bar
3462`),
3463 output: parser.File{
3464 IsRelaxed: true,
3465 Groups: []parser.Group{
3466 {
3467 Rules: []parser.Rule{
3468 {
3469 Lines: diags.LineRange{First: 2, Last: 3},
3470 RecordingRule: &parser.RecordingRule{
3471 Record: parser.YamlNode{
3472 Value: "foo",
3473 Pos: diags.PositionRanges{
3474 {Line: 3, FirstColumn: 11, LastColumn: 13},
3475 },
3476 },
3477 Expr: parser.PromQLExpr{
3478 Value: &parser.YamlNode{
3479 Value: "foo",
3480 Pos: diags.PositionRanges{
3481 {Line: 2, FirstColumn: 9, LastColumn: 11},
3482 },
3483 },
3484 },
3485 },
3486 },
3487 },
3488 },
3489 {
3490 Rules: []parser.Rule{
3491 {
3492 Lines: diags.LineRange{First: 5, Last: 7},
3493 Error: parser.ParseError{Err: errors.New("duplicated expr key"), Line: 7},
3494 },
3495 },
3496 },
3497 },
3498 },
3499 },
3500 {
3501 input: []byte(`---
3502- expr: foo
3503 record: foo
3504---
3505- expr: bar
3506 alert: foo
3507`),
3508 output: parser.File{
3509 IsRelaxed: true,
3510 Groups: []parser.Group{
3511 {
3512 Rules: []parser.Rule{
3513 {
3514 Lines: diags.LineRange{First: 2, Last: 3},
3515 RecordingRule: &parser.RecordingRule{
3516 Record: parser.YamlNode{
3517 Value: "foo",
3518 Pos: diags.PositionRanges{
3519 {Line: 3, FirstColumn: 11, LastColumn: 13},
3520 },
3521 },
3522 Expr: parser.PromQLExpr{
3523 Value: &parser.YamlNode{
3524 Value: "foo",
3525 Pos: diags.PositionRanges{
3526 {Line: 2, FirstColumn: 9, LastColumn: 11},
3527 },
3528 },
3529 },
3530 },
3531 },
3532 },
3533 },
3534 {
3535 Rules: []parser.Rule{
3536 {
3537 Lines: diags.LineRange{First: 5, Last: 6},
3538 AlertingRule: &parser.AlertingRule{
3539 Alert: parser.YamlNode{
3540 Value: "foo",
3541 Pos: diags.PositionRanges{
3542 {Line: 6, FirstColumn: 10, LastColumn: 12},
3543 },
3544 },
3545 Expr: parser.PromQLExpr{
3546 Value: &parser.YamlNode{
3547 Value: "bar",
3548 Pos: diags.PositionRanges{
3549 {Line: 5, FirstColumn: 9, LastColumn: 11},
3550 },
3551 },
3552 },
3553 },
3554 },
3555 },
3556 },
3557 },
3558 },
3559 },
3560 {
3561 input: []byte(`---
3562groups:
3563- name: v1
3564 rules:
3565 - record: up:count
3566 expr: count(up)
3567 labels:
3568 foo:
3569 bar: foo
3570`),
3571 output: parser.File{
3572 IsRelaxed: true,
3573 Groups: []parser.Group{
3574 {
3575 Name: parser.YamlNode{
3576 Value: "v1",
3577 Pos: diags.PositionRanges{
3578 {Line: 3, FirstColumn: 9, LastColumn: 10},
3579 },
3580 },
3581 Rules: []parser.Rule{
3582 {
3583 Lines: diags.LineRange{First: 5, Last: 9},
3584 Error: parser.ParseError{
3585 Err: errors.New("labels foo value must be a string, got mapping instead"),
3586 Line: 9,
3587 },
3588 },
3589 },
3590 },
3591 },
3592 },
3593 },
3594 {
3595 input: []byte(`
3596groups:
3597- name: v1
3598 rules:
3599 - record: up:count
3600 expr: count(up)
3601`),
3602 strict: true,
3603 output: parser.File{
3604 Groups: []parser.Group{
3605 {
3606 Name: parser.YamlNode{
3607 Value: "v1",
3608 Pos: diags.PositionRanges{
3609 {Line: 3, FirstColumn: 9, LastColumn: 10},
3610 },
3611 },
3612 Rules: []parser.Rule{
3613 {
3614 Lines: diags.LineRange{First: 5, Last: 6},
3615 RecordingRule: &parser.RecordingRule{
3616 Record: parser.YamlNode{
3617 Value: "up:count",
3618 Pos: diags.PositionRanges{
3619 {Line: 5, FirstColumn: 13, LastColumn: 20},
3620 },
3621 },
3622 Expr: parser.PromQLExpr{
3623 Value: &parser.YamlNode{
3624 Value: "count(up)",
3625 Pos: diags.PositionRanges{
3626 {Line: 6, FirstColumn: 11, LastColumn: 19},
3627 },
3628 },
3629 },
3630 },
3631 },
3632 },
3633 },
3634 },
3635 },
3636 },
3637 {
3638 input: []byte(`
3639groups:
3640- name: v1
3641 rules:
3642 - record: up:count
3643`),
3644 strict: true,
3645 output: parser.File{
3646 Groups: []parser.Group{
3647 {
3648 Name: parser.YamlNode{
3649 Value: "v1",
3650 Pos: diags.PositionRanges{
3651 {Line: 3, FirstColumn: 9, LastColumn: 10},
3652 },
3653 },
3654 Rules: []parser.Rule{
3655 {
3656 Lines: diags.LineRange{First: 5, Last: 5},
3657 Error: parser.ParseError{
3658 Err: errors.New("missing expr key"),
3659 Line: 5,
3660 },
3661 },
3662 },
3663 },
3664 },
3665 },
3666 },
3667 {
3668 input: []byte(`
3669- record: up:count
3670 expr: count(up)
3671`),
3672 strict: true,
3673 output: parser.File{
3674 Error: parser.ParseError{
3675 Err: errors.New("top level field must be a groups key, got list"),
3676 Line: 2,
3677 },
3678 },
3679 },
3680 {
3681 input: []byte(`
3682rules:
3683 - record: up:count
3684 expr: count(up)
3685`),
3686 strict: true,
3687 output: parser.File{
3688 Error: parser.ParseError{
3689 Err: errors.New("unexpected key rules"),
3690 Line: 2,
3691 },
3692 },
3693 },
3694 {
3695 input: []byte(`
3696groups:
3697 - record: up:count
3698 expr: count(up)
3699`),
3700 strict: true,
3701 output: parser.File{
3702 Groups: []parser.Group{
3703 {
3704 Error: parser.ParseError{
3705 Err: errors.New("invalid group key record"),
3706 Line: 3,
3707 },
3708 },
3709 },
3710 },
3711 },
3712 {
3713 input: []byte(`
3714groups:
3715- rules:
3716 - record: up:count
3717 expr: count(up)
3718`),
3719 strict: true,
3720 output: parser.File{
3721 Groups: []parser.Group{
3722 {
3723 Error: parser.ParseError{
3724 Err: errors.New("incomplete group definition, name is required and must be set"),
3725 Line: 3,
3726 },
3727 Rules: []parser.Rule{
3728 {
3729 Lines: diags.LineRange{First: 4, Last: 5},
3730 RecordingRule: &parser.RecordingRule{
3731 Record: parser.YamlNode{
3732 Value: "up:count",
3733 Pos: diags.PositionRanges{
3734 {Line: 4, FirstColumn: 13, LastColumn: 20},
3735 },
3736 },
3737 Expr: parser.PromQLExpr{
3738 Value: &parser.YamlNode{
3739 Value: "count(up)",
3740 Pos: diags.PositionRanges{
3741 {Line: 5, FirstColumn: 11, LastColumn: 19},
3742 },
3743 },
3744 },
3745 },
3746 },
3747 },
3748 },
3749 },
3750 },
3751 },
3752 {
3753 input: []byte(`
3754groups:
3755- name: foo
3756`),
3757 strict: true,
3758 output: parser.File{
3759 Groups: []parser.Group{
3760 {
3761 Name: parser.YamlNode{
3762 Value: "foo",
3763 Pos: diags.PositionRanges{
3764 {Line: 3, FirstColumn: 9, LastColumn: 11},
3765 },
3766 },
3767 },
3768 },
3769 },
3770 },
3771 {
3772 input: []byte(`
3773groups: {}
3774`),
3775 strict: true,
3776 output: parser.File{
3777 Error: parser.ParseError{
3778 Err: errors.New("groups value must be a list, got mapping"),
3779 Line: 2,
3780 },
3781 },
3782 },
3783 {
3784 input: []byte(`
3785groups:
3786- name: []
3787`),
3788 strict: true,
3789 output: parser.File{
3790 Groups: []parser.Group{
3791 {
3792 Error: parser.ParseError{
3793 Err: errors.New("group name must be a string, got list"),
3794 Line: 3,
3795 },
3796 },
3797 },
3798 },
3799 },
3800 {
3801 input: []byte(`
3802groups:
3803- name: foo
3804 name: bar
3805 name: bob
3806`),
3807 strict: true,
3808 output: parser.File{
3809 Groups: []parser.Group{
3810 {
3811 Name: parser.YamlNode{
3812 Value: "bar",
3813 Pos: diags.PositionRanges{
3814 {Line: 4, FirstColumn: 9, LastColumn: 11},
3815 },
3816 },
3817 Error: parser.ParseError{
3818 Err: errors.New("duplicated key name"),
3819 Line: 4,
3820 },
3821 },
3822 },
3823 },
3824 },
3825 {
3826 input: []byte(`
3827groups:
3828- name: v1
3829 rules:
3830 rules:
3831 - record: up:count
3832 expr: count(up)
3833`),
3834 strict: true,
3835 output: parser.File{
3836 Groups: []parser.Group{
3837 {
3838 Name: parser.YamlNode{
3839 Value: "v1",
3840 Pos: diags.PositionRanges{
3841 {Line: 3, FirstColumn: 9, LastColumn: 10},
3842 },
3843 },
3844 Error: parser.ParseError{
3845 Err: errors.New("rules must be a list, got mapping"),
3846 Line: 4,
3847 },
3848 },
3849 },
3850 },
3851 },
3852 {
3853 input: []byte(`
3854groups:
3855- name: v1
3856 rules:
3857 - rules:
3858 - record: up:count
3859 expr: count(up)
3860`),
3861 strict: true,
3862 output: parser.File{
3863 Groups: []parser.Group{
3864 {
3865 Name: parser.YamlNode{
3866 Value: "v1",
3867 Pos: diags.PositionRanges{
3868 {Line: 3, FirstColumn: 9, LastColumn: 10},
3869 },
3870 },
3871 Rules: []parser.Rule{
3872 {
3873 Error: parser.ParseError{
3874 Err: errors.New("invalid rule key rules"),
3875 Line: 5,
3876 },
3877 },
3878 },
3879 },
3880 },
3881 },
3882 },
3883 {
3884 input: []byte(`
3885groups:
3886- name: v1
3887 rules:
3888 - rules:
3889 - record: up:count
3890 expr: count(up)
3891`),
3892 strict: true,
3893 output: parser.File{
3894 Error: parser.ParseError{
3895 Err: errors.New("found a tab character that violates indentation"),
3896 Line: 6,
3897 },
3898 },
3899 },
3900 {
3901 input: []byte(`
3902---
3903groups:
3904- name: v1
3905 rules:
3906 - record: up:count
3907 expr: count(up)
3908---
3909groups:
3910- name: v1
3911 rules:
3912 - rules:
3913 - record: up:count
3914 expr: count(up)
3915`),
3916 strict: true,
3917 output: parser.File{
3918 Groups: []parser.Group{
3919 {
3920 Name: parser.YamlNode{
3921 Value: "v1",
3922 Pos: diags.PositionRanges{
3923 {Line: 4, FirstColumn: 9, LastColumn: 10},
3924 },
3925 },
3926 Rules: []parser.Rule{
3927 {
3928 Lines: diags.LineRange{First: 6, Last: 7},
3929 RecordingRule: &parser.RecordingRule{
3930 Record: parser.YamlNode{
3931 Value: "up:count",
3932 Pos: diags.PositionRanges{
3933 {Line: 6, FirstColumn: 15, LastColumn: 22},
3934 },
3935 },
3936 Expr: parser.PromQLExpr{
3937 Value: &parser.YamlNode{
3938 Value: "count(up)",
3939 Pos: diags.PositionRanges{
3940 {Line: 7, FirstColumn: 13, LastColumn: 21},
3941 },
3942 },
3943 },
3944 },
3945 },
3946 },
3947 },
3948 {
3949 Name: parser.YamlNode{
3950 Value: "v1",
3951 Pos: diags.PositionRanges{
3952 {Line: 10, FirstColumn: 9, LastColumn: 10},
3953 },
3954 },
3955 Rules: []parser.Rule{
3956 {
3957 Error: parser.ParseError{
3958 Err: errors.New("invalid rule key rules"),
3959 Line: 12,
3960 },
3961 },
3962 },
3963 },
3964 },
3965 Error: parser.ParseError{
3966 Line: 8,
3967 Err: errors.New("multi-document YAML files are not allowed"),
3968 },
3969 },
3970 },
3971 {
3972 input: []byte(`
3973---
3974groups: []
3975---
3976groups:
3977- name: foo
3978 rules:
3979 - labels: !!binary "SGVsbG8sIFdvcmxkIQ=="
3980 record: foo
3981 expr: foo
3982`),
3983 strict: true,
3984 output: parser.File{
3985 Groups: []parser.Group{
3986 {
3987 Name: parser.YamlNode{
3988 Value: "foo",
3989 Pos: diags.PositionRanges{
3990 {Line: 6, FirstColumn: 9, LastColumn: 11},
3991 },
3992 },
3993 Rules: []parser.Rule{
3994 {
3995 Lines: diags.LineRange{First: 8, Last: 10},
3996 Error: parser.ParseError{
3997 Line: 8,
3998 Err: errors.New("labels value must be a mapping, got binary data instead"),
3999 },
4000 },
4001 },
4002 },
4003 },
4004 Error: parser.ParseError{
4005 Line: 4,
4006 Err: errors.New("multi-document YAML files are not allowed"),
4007 },
4008 },
4009 },
4010 {
4011 input: []byte("[]"),
4012 strict: true,
4013 output: parser.File{
4014 Error: parser.ParseError{
4015 Err: errors.New("top level field must be a groups key, got list"),
4016 Line: 1,
4017 },
4018 },
4019 },
4020 {
4021 input: []byte("\n\n[]"),
4022 strict: true,
4023 output: parser.File{
4024 Error: parser.ParseError{
4025 Err: errors.New("top level field must be a groups key, got list"),
4026 Line: 3,
4027 },
4028 },
4029 },
4030 {
4031 input: []byte("groups: {}"),
4032 strict: true,
4033 output: parser.File{
4034 Error: parser.ParseError{
4035 Err: errors.New("groups value must be a list, got mapping"),
4036 Line: 1,
4037 },
4038 },
4039 },
4040 {
4041 input: []byte("groups: []"),
4042 strict: true,
4043 },
4044 {
4045 input: []byte("xgroups: {}"),
4046 strict: true,
4047 output: parser.File{
4048 Error: parser.ParseError{
4049 Err: errors.New("unexpected key xgroups"),
4050 Line: 1,
4051 },
4052 },
4053 },
4054 {
4055 input: []byte("\nbob\n"),
4056 strict: true,
4057 output: parser.File{
4058 Error: parser.ParseError{
4059 Err: errors.New("top level field must be a groups key, got string"),
4060 Line: 2,
4061 },
4062 },
4063 },
4064 {
4065 input: []byte(`groups: []
4066
4067rules: []
4068`),
4069 strict: true,
4070 output: parser.File{
4071 Error: parser.ParseError{
4072 Err: errors.New("unexpected key rules"),
4073 Line: 3,
4074 },
4075 },
4076 },
4077 {
4078 input: []byte(`
4079groups:
4080- name: foo
4081 rules: []
4082`),
4083 strict: true,
4084 output: parser.File{
4085 Groups: []parser.Group{
4086 {
4087 Name: parser.YamlNode{
4088 Value: "foo",
4089 Pos: diags.PositionRanges{
4090 {Line: 3, FirstColumn: 9, LastColumn: 11},
4091 },
4092 },
4093 },
4094 },
4095 },
4096 },
4097 {
4098 input: []byte(`
4099groups:
4100- name:
4101 rules: []
4102`),
4103 strict: true,
4104 output: parser.File{
4105 Groups: []parser.Group{
4106 {
4107 Error: parser.ParseError{
4108 Err: errors.New("group name must be a string, got null"),
4109 Line: 3,
4110 },
4111 },
4112 },
4113 },
4114 },
4115 {
4116 // YAML alias on group name in strict mode is resolved correctly
4117 input: []byte(`
4118groups:
4119- name: &gname mygroup
4120 rules:
4121 - record: foo
4122 expr: sum(up)
4123- name: *gname
4124 rules:
4125 - record: bar
4126 expr: sum(down)
4127`),
4128 strict: true,
4129 output: parser.File{
4130 Error: parser.ParseError{
4131 Err: errors.New("duplicated group name"),
4132 Line: 7,
4133 },
4134 },
4135 },
4136 {
4137 // YAML alias on group interval in strict mode is resolved correctly
4138 input: []byte(`
4139groups:
4140- name: mygroup
4141 interval: &ivl 1m
4142 rules:
4143 - record: foo
4144 expr: sum(up)
4145`),
4146 strict: true,
4147 output: parser.File{
4148 Groups: []parser.Group{
4149 {
4150 Name: parser.YamlNode{
4151 Value: "mygroup",
4152 Pos: diags.PositionRanges{
4153 {Line: 3, FirstColumn: 9, LastColumn: 15},
4154 },
4155 },
4156 Interval: &parser.YamlDuration{
4157 Raw: "1m",
4158 Value: 1 * time.Minute,
4159 Pos: diags.PositionRanges{
4160 {Line: 4, FirstColumn: 18, LastColumn: 19},
4161 },
4162 },
4163 Rules: []parser.Rule{
4164 {
4165 Lines: diags.LineRange{First: 6, Last: 7},
4166 RecordingRule: &parser.RecordingRule{
4167 Record: parser.YamlNode{
4168 Value: "foo",
4169 Pos: diags.PositionRanges{
4170 {Line: 6, FirstColumn: 13, LastColumn: 15},
4171 },
4172 },
4173 Expr: parser.PromQLExpr{
4174 Value: &parser.YamlNode{
4175 Value: "sum(up)",
4176 Pos: diags.PositionRanges{
4177 {Line: 7, FirstColumn: 11, LastColumn: 17},
4178 },
4179 },
4180 },
4181 },
4182 },
4183 },
4184 },
4185 },
4186 },
4187 },
4188 {
4189 input: []byte(`
4190groups:
4191- name: foo
4192 rules:
4193 - record: foo
4194 expr: sum(up)
4195 labels:
4196 job: foo
4197`),
4198 strict: true,
4199 output: parser.File{
4200 Groups: []parser.Group{
4201 {
4202 Name: parser.YamlNode{
4203 Value: "foo",
4204 Pos: diags.PositionRanges{
4205 {Line: 3, FirstColumn: 9, LastColumn: 11},
4206 },
4207 },
4208 Rules: []parser.Rule{
4209 {
4210 Lines: diags.LineRange{First: 5, Last: 8},
4211 RecordingRule: &parser.RecordingRule{
4212 Record: parser.YamlNode{
4213 Value: "foo",
4214 Pos: diags.PositionRanges{
4215 {Line: 5, FirstColumn: 13, LastColumn: 15},
4216 },
4217 },
4218 Expr: parser.PromQLExpr{
4219 Value: &parser.YamlNode{
4220 Value: "sum(up)",
4221 Pos: diags.PositionRanges{
4222 {Line: 6, FirstColumn: 11, LastColumn: 17},
4223 },
4224 },
4225 },
4226 Labels: &parser.YamlMap{
4227 Key: &parser.YamlNode{
4228 Value: "labels",
4229 Pos: diags.PositionRanges{
4230 {Line: 7, FirstColumn: 5, LastColumn: 10},
4231 },
4232 },
4233 Items: []*parser.YamlKeyValue{
4234 {
4235 Key: &parser.YamlNode{
4236 Value: "job",
4237 Pos: diags.PositionRanges{
4238 {Line: 8, FirstColumn: 7, LastColumn: 9},
4239 },
4240 },
4241 Value: &parser.YamlNode{
4242 Value: "foo",
4243 Pos: diags.PositionRanges{
4244 {Line: 8, FirstColumn: 12, LastColumn: 14},
4245 },
4246 },
4247 },
4248 },
4249 },
4250 },
4251 },
4252 },
4253 },
4254 },
4255 },
4256 },
4257 {
4258 input: []byte(`
4259groups:
4260- name: foo
4261 rules:
4262 - record: foo
4263 expr: sum(up)
4264 xxx: 1
4265 labels:
4266 job: foo
4267`),
4268 strict: true,
4269 output: parser.File{
4270 Groups: []parser.Group{
4271 {
4272 Name: parser.YamlNode{
4273 Value: "foo",
4274 Pos: diags.PositionRanges{
4275 {Line: 3, FirstColumn: 9, LastColumn: 11},
4276 },
4277 },
4278 Rules: []parser.Rule{
4279 {
4280 Error: parser.ParseError{
4281 Err: errors.New("invalid rule key xxx"),
4282 Line: 7,
4283 },
4284 },
4285 },
4286 },
4287 },
4288 },
4289 },
4290 {
4291 input: []byte(`
4292groups:
4293- name: foo
4294 rules:
4295 record: foo
4296 expr: sum(up)
4297 xxx: 1
4298 labels:
4299 job: foo
4300`),
4301 strict: true,
4302 output: parser.File{
4303 Groups: []parser.Group{
4304 {
4305 Name: parser.YamlNode{
4306 Value: "foo",
4307 Pos: diags.PositionRanges{
4308 {Line: 3, FirstColumn: 9, LastColumn: 11},
4309 },
4310 },
4311 Error: parser.ParseError{
4312 Err: errors.New("rules must be a list, got mapping"),
4313 Line: 4,
4314 },
4315 },
4316 },
4317 },
4318 },
4319 {
4320 input: []byte(`
4321groups:
4322- name: foo
4323 rules:
4324 - record: foo
4325 expr: sum(up)
4326 labels:
4327 job:
4328 foo: bar
4329`),
4330 strict: true,
4331 output: parser.File{
4332 Groups: []parser.Group{
4333 {
4334 Name: parser.YamlNode{
4335 Value: "foo",
4336 Pos: diags.PositionRanges{
4337 {Line: 3, FirstColumn: 9, LastColumn: 11},
4338 },
4339 },
4340 Rules: []parser.Rule{
4341 {
4342 Lines: diags.LineRange{First: 5, Last: 9},
4343 Error: parser.ParseError{
4344 Line: 9,
4345 Err: errors.New("labels job value must be a string, got mapping instead"),
4346 },
4347 },
4348 },
4349 },
4350 },
4351 },
4352 },
4353 {
4354 input: []byte(`
4355groups:
4356- name: foo
4357 rules:
4358 - record: foo
4359 expr:
4360 sum: sum(up)
4361`),
4362 strict: true,
4363 output: parser.File{
4364 Groups: []parser.Group{
4365 {
4366 Name: parser.YamlNode{
4367 Value: "foo",
4368 Pos: diags.PositionRanges{
4369 {Line: 3, FirstColumn: 9, LastColumn: 11},
4370 },
4371 },
4372 Rules: []parser.Rule{
4373 {
4374 Lines: diags.LineRange{First: 5, Last: 7},
4375 Error: parser.ParseError{
4376 Line: 7,
4377 Err: errors.New("expr value must be a string, got mapping instead"),
4378 },
4379 },
4380 },
4381 },
4382 },
4383 },
4384 },
4385 {
4386 input: []byte(`
4387groups:
4388- name: foo
4389 rules: []
4390- name: foo
4391 rules: []
4392`),
4393 strict: true,
4394 output: parser.File{
4395 Error: parser.ParseError{
4396 Err: errors.New("duplicated group name"),
4397 Line: 5,
4398 },
4399 },
4400 },
4401 {
4402 input: []byte(`
4403groups:
4404- name: foo
4405 rules:
4406 - record: foo
4407 expr: sum(up)
4408 labels:
4409 foo: bob
4410 foo: bar
4411`),
4412 strict: true,
4413 output: parser.File{
4414 Groups: []parser.Group{
4415 {
4416 Name: parser.YamlNode{
4417 Value: "foo",
4418 Pos: diags.PositionRanges{
4419 {Line: 3, FirstColumn: 9, LastColumn: 11},
4420 },
4421 },
4422 Rules: []parser.Rule{
4423 {
4424 Lines: diags.LineRange{First: 8, Last: 9},
4425 Error: parser.ParseError{
4426 Line: 9,
4427 Err: errors.New("duplicated labels key foo"),
4428 },
4429 },
4430 },
4431 },
4432 },
4433 },
4434 },
4435 {
4436 input: []byte(`
4437groups:
4438- name: v2
4439 rules:
4440 - record: up:count
4441 expr: count(up)
4442 expr: sum(up)`),
4443 strict: true,
4444 output: parser.File{
4445 Groups: []parser.Group{
4446 {
4447 Name: parser.YamlNode{
4448 Value: "v2",
4449 Pos: diags.PositionRanges{
4450 {Line: 3, FirstColumn: 9, LastColumn: 10},
4451 },
4452 },
4453 Rules: []parser.Rule{
4454 {
4455 Lines: diags.LineRange{First: 5, Last: 7},
4456 Error: parser.ParseError{
4457 Line: 7,
4458 Err: errors.New("duplicated expr key"),
4459 },
4460 },
4461 },
4462 },
4463 },
4464 },
4465 },
4466 {
4467 input: []byte(`
4468groups:
4469- name: v2
4470 rules:
4471 - record: up:count
4472 expr: count(up)
4473bogus: 1
4474`),
4475 strict: true,
4476 output: parser.File{
4477 Error: parser.ParseError{
4478 Err: errors.New("unexpected key bogus"),
4479 Line: 7,
4480 },
4481 },
4482 },
4483 {
4484 input: []byte(`
4485groups:
4486- name: v2
4487 rules:
4488 - record: up:count
4489 expr: count(up)
4490 bogus: 1
4491`),
4492 strict: true,
4493 output: parser.File{
4494 Groups: []parser.Group{
4495 {
4496 Name: parser.YamlNode{
4497 Value: "v2",
4498 Pos: diags.PositionRanges{
4499 {Line: 3, FirstColumn: 9, LastColumn: 10},
4500 },
4501 },
4502 Rules: []parser.Rule{
4503 {
4504 Error: parser.ParseError{
4505 Err: errors.New("invalid rule key bogus"),
4506 Line: 7,
4507 },
4508 },
4509 },
4510 },
4511 },
4512 },
4513 },
4514 {
4515 input: []byte(`
4516groups:
4517- name: v2
4518 rules:
4519 - alert: up:count
4520 for: 5m
4521 keep_firing_for: 5m
4522 expr: count(up)
4523 labels: {}
4524 annotations: {}
4525 bogus: 1
4526`),
4527 strict: true,
4528 output: parser.File{
4529 Groups: []parser.Group{
4530 {
4531 Name: parser.YamlNode{
4532 Value: "v2",
4533 Pos: diags.PositionRanges{
4534 {Line: 3, FirstColumn: 9, LastColumn: 10},
4535 },
4536 },
4537 Rules: []parser.Rule{
4538 {
4539 Error: parser.ParseError{
4540 Err: errors.New("invalid rule key bogus"),
4541 Line: 11,
4542 },
4543 },
4544 },
4545 },
4546 },
4547 },
4548 },
4549 {
4550 input: []byte(`
4551groups:
4552
4553- name: CloudflareKafkaZookeeperExporter
4554
4555 rules:
4556`),
4557 strict: true,
4558 output: parser.File{
4559 Groups: []parser.Group{
4560 {
4561 Name: parser.YamlNode{
4562 Value: "CloudflareKafkaZookeeperExporter",
4563 Pos: diags.PositionRanges{
4564 {Line: 4, FirstColumn: 9, LastColumn: 40},
4565 },
4566 },
4567 },
4568 },
4569 },
4570 },
4571 {
4572 input: []byte(`
4573groups:
4574- name: foo
4575 rules:
4576 expr: 1
4577`),
4578 strict: true,
4579 output: parser.File{
4580 Groups: []parser.Group{
4581 {
4582 Name: parser.YamlNode{
4583 Value: "foo",
4584 Pos: diags.PositionRanges{
4585 {Line: 3, FirstColumn: 9, LastColumn: 11},
4586 },
4587 },
4588 Error: parser.ParseError{
4589 Err: errors.New("rules must be a list, got mapping"),
4590 Line: 4,
4591 },
4592 },
4593 },
4594 },
4595 },
4596 {
4597 input: []byte(`
4598groups:
4599- name: foo
4600 rules:
4601 - expr: 1
4602`),
4603 strict: true,
4604 output: parser.File{
4605 Groups: []parser.Group{
4606 {
4607 Name: parser.YamlNode{
4608 Value: "foo",
4609 Pos: diags.PositionRanges{
4610 {Line: 3, FirstColumn: 9, LastColumn: 11},
4611 },
4612 },
4613 Rules: []parser.Rule{
4614 {
4615 Lines: diags.LineRange{First: 5, Last: 5},
4616 Error: parser.ParseError{
4617 Line: 5,
4618 Err: errors.New("incomplete rule, no alert or record key"),
4619 },
4620 },
4621 },
4622 },
4623 },
4624 },
4625 },
4626 {
4627 input: []byte(`
4628groups:
4629- name: foo
4630 rules:
4631 - expr: null
4632`),
4633 strict: true,
4634 output: parser.File{
4635 Groups: []parser.Group{
4636 {
4637 Name: parser.YamlNode{
4638 Value: "foo",
4639 Pos: diags.PositionRanges{
4640 {Line: 3, FirstColumn: 9, LastColumn: 11},
4641 },
4642 },
4643 Rules: []parser.Rule{
4644 {
4645 Lines: diags.LineRange{First: 5, Last: 5},
4646 Error: parser.ParseError{
4647 Line: 5,
4648 Err: errors.New("incomplete rule, no alert or record key"),
4649 },
4650 },
4651 },
4652 },
4653 },
4654 },
4655 },
4656 {
4657 input: []byte(`
4658groups:
4659- name: foo
4660 rules:
4661 - 1: null
4662`),
4663 strict: true,
4664 output: parser.File{
4665 Groups: []parser.Group{
4666 {
4667 Name: parser.YamlNode{
4668 Value: "foo",
4669 Pos: diags.PositionRanges{
4670 {Line: 3, FirstColumn: 9, LastColumn: 11},
4671 },
4672 },
4673 Rules: []parser.Rule{
4674 {
4675 Error: parser.ParseError{
4676 Err: errors.New("invalid rule key 1"),
4677 Line: 5,
4678 },
4679 },
4680 },
4681 },
4682 },
4683 },
4684 },
4685 {
4686 input: []byte(`
4687groups:
4688- name: foo
4689 rules:
4690 - true: !!binary "SGVsbG8sIFdvcmxkIQ=="
4691`),
4692 strict: true,
4693 output: parser.File{
4694 Groups: []parser.Group{
4695 {
4696 Name: parser.YamlNode{
4697 Value: "foo",
4698 Pos: diags.PositionRanges{
4699 {Line: 3, FirstColumn: 9, LastColumn: 11},
4700 },
4701 },
4702 Rules: []parser.Rule{
4703 {
4704 Error: parser.ParseError{
4705 Err: errors.New("invalid rule key true"),
4706 Line: 5,
4707 },
4708 },
4709 },
4710 },
4711 },
4712 },
4713 },
4714 {
4715 input: []byte(`
4716groups:
4717- name: foo
4718 rules:
4719 - expr: !!binary "SGVsbG8sIFdvcmxkIQ=="
4720`),
4721 strict: true,
4722 output: parser.File{
4723 Groups: []parser.Group{
4724 {
4725 Name: parser.YamlNode{
4726 Value: "foo",
4727 Pos: diags.PositionRanges{
4728 {Line: 3, FirstColumn: 9, LastColumn: 11},
4729 },
4730 },
4731 Rules: []parser.Rule{
4732 {
4733 Lines: diags.LineRange{First: 5, Last: 5},
4734 Error: parser.ParseError{
4735 Line: 5,
4736 Err: errors.New("incomplete rule, no alert or record key"),
4737 },
4738 },
4739 },
4740 },
4741 },
4742 },
4743 },
4744 {
4745 input: []byte(`
4746groups:
4747- name: foo
4748 rules:
4749 - expr: !!binary "SGVsbG8sIFdvcmxkIQ=="
4750 record: foo
4751`),
4752 strict: true,
4753 output: parser.File{
4754 Groups: []parser.Group{
4755 {
4756 Name: parser.YamlNode{
4757 Value: "foo",
4758 Pos: diags.PositionRanges{
4759 {Line: 3, FirstColumn: 9, LastColumn: 11},
4760 },
4761 },
4762 Rules: []parser.Rule{
4763 {
4764 Lines: diags.LineRange{First: 5, Last: 6},
4765 Error: parser.ParseError{
4766 Line: 5,
4767 Err: errors.New("expr value must be a string, got binary data instead"),
4768 },
4769 },
4770 },
4771 },
4772 },
4773 },
4774 },
4775 {
4776 input: []byte(`
4777groups:
4778- name: foo
4779 rules:
4780 - labels: !!binary "SGVsbG8sIFdvcmxkIQ=="
4781 record: foo
4782 expr: foo
4783`),
4784 strict: true,
4785 output: parser.File{
4786 Groups: []parser.Group{
4787 {
4788 Name: parser.YamlNode{
4789 Value: "foo",
4790 Pos: diags.PositionRanges{
4791 {Line: 3, FirstColumn: 9, LastColumn: 11},
4792 },
4793 },
4794 Rules: []parser.Rule{
4795 {
4796 Lines: diags.LineRange{First: 5, Last: 7},
4797 Error: parser.ParseError{
4798 Line: 5,
4799 Err: errors.New("labels value must be a mapping, got binary data instead"),
4800 },
4801 },
4802 },
4803 },
4804 },
4805 },
4806 },
4807 {
4808 input: []byte(`
4809---
4810groups:
4811- name: foo
4812 rules:
4813 - record: foo
4814 expr: bar
4815---
4816groups:
4817- name: foo
4818 rules:
4819 - record: foo
4820 expr: bar
4821`),
4822 strict: true,
4823 output: parser.File{
4824 Error: parser.ParseError{
4825 Line: 8,
4826 Err: errors.New("multi-document YAML files are not allowed"),
4827 },
4828 Groups: []parser.Group{
4829 {
4830 Name: parser.YamlNode{
4831 Value: "foo",
4832 Pos: diags.PositionRanges{
4833 {Line: 4, FirstColumn: 9, LastColumn: 11},
4834 },
4835 },
4836 Rules: []parser.Rule{
4837 {
4838 Lines: diags.LineRange{First: 6, Last: 7},
4839 RecordingRule: &parser.RecordingRule{
4840 Record: parser.YamlNode{
4841 Value: "foo",
4842 Pos: diags.PositionRanges{
4843 {Line: 6, FirstColumn: 15, LastColumn: 17},
4844 },
4845 },
4846 Expr: parser.PromQLExpr{
4847 Value: &parser.YamlNode{
4848 Value: "bar",
4849 Pos: diags.PositionRanges{
4850 {Line: 7, FirstColumn: 13, LastColumn: 15},
4851 },
4852 },
4853 },
4854 },
4855 },
4856 },
4857 },
4858 {
4859 Name: parser.YamlNode{
4860 Value: "foo",
4861 Pos: diags.PositionRanges{
4862 {Line: 10, FirstColumn: 9, LastColumn: 11},
4863 },
4864 },
4865 Rules: []parser.Rule{
4866 {
4867 Lines: diags.LineRange{First: 12, Last: 13},
4868 RecordingRule: &parser.RecordingRule{
4869 Record: parser.YamlNode{
4870 Value: "foo",
4871 Pos: diags.PositionRanges{
4872 {Line: 12, FirstColumn: 15, LastColumn: 17},
4873 },
4874 },
4875 Expr: parser.PromQLExpr{
4876 Value: &parser.YamlNode{
4877 Value: "bar",
4878 Pos: diags.PositionRanges{
4879 {Line: 13, FirstColumn: 13, LastColumn: 15},
4880 },
4881 },
4882 },
4883 },
4884 },
4885 },
4886 },
4887 },
4888 },
4889 },
4890 {
4891 input: []byte(`
4892groups:
4893- name: foo
4894 rules:
4895 - record: foo
4896 expr: foo
4897 expr: foo
4898`),
4899 strict: true,
4900 output: parser.File{
4901 Groups: []parser.Group{
4902 {
4903 Name: parser.YamlNode{
4904 Value: "foo",
4905 Pos: diags.PositionRanges{
4906 {Line: 3, FirstColumn: 9, LastColumn: 11},
4907 },
4908 },
4909 Rules: []parser.Rule{
4910 {
4911 Lines: diags.LineRange{First: 5, Last: 7},
4912 Error: parser.ParseError{
4913 Line: 7,
4914 Err: errors.New("duplicated expr key"),
4915 },
4916 },
4917 },
4918 },
4919 },
4920 },
4921 },
4922 {
4923 input: []byte(`
4924groups:
4925- name: foo
4926 rules:
4927 - record: foo
4928 keep_firing_for: 1m
4929 keep_firing_for: 2m
4930`),
4931 strict: true,
4932 output: parser.File{
4933 Groups: []parser.Group{
4934 {
4935 Name: parser.YamlNode{
4936 Value: "foo",
4937 Pos: diags.PositionRanges{
4938 {Line: 3, FirstColumn: 9, LastColumn: 11},
4939 },
4940 },
4941 Rules: []parser.Rule{
4942 {
4943 Lines: diags.LineRange{First: 5, Last: 7},
4944 Error: parser.ParseError{
4945 Line: 7,
4946 Err: errors.New("duplicated keep_firing_for key"),
4947 },
4948 },
4949 },
4950 },
4951 },
4952 },
4953 },
4954 {
4955 input: []byte(`
4956groups:
4957- name: foo
4958 rules:
4959 - record: foo
4960 keep_firing_for: 1m
4961 record: 2m
4962`),
4963 strict: true,
4964 output: parser.File{
4965 Groups: []parser.Group{
4966 {
4967 Name: parser.YamlNode{
4968 Value: "foo",
4969 Pos: diags.PositionRanges{
4970 {Line: 3, FirstColumn: 9, LastColumn: 11},
4971 },
4972 },
4973 Rules: []parser.Rule{
4974 {
4975 Lines: diags.LineRange{First: 5, Last: 7},
4976 Error: parser.ParseError{
4977 Line: 7,
4978 Err: errors.New("duplicated record key"),
4979 },
4980 },
4981 },
4982 },
4983 },
4984 },
4985 },
4986 {
4987 input: []byte(`
4988groups:
4989- name: foo
4990 rules:
4991 - []
4992`),
4993 strict: true,
4994 output: parser.File{
4995 Groups: []parser.Group{
4996 {
4997 Name: parser.YamlNode{
4998 Value: "foo",
4999 Pos: diags.PositionRanges{
5000 {Line: 3, FirstColumn: 9, LastColumn: 11},
5001 },
5002 },
5003 Rules: []parser.Rule{
5004 {
5005 Error: parser.ParseError{
5006 Err: errors.New("rule definition must be a mapping, got list"),
5007 Line: 5,
5008 },
5009 },
5010 },
5011 },
5012 },
5013 },
5014 },
5015 {
5016 input: []byte(`
50171: 0
5018`),
5019 strict: true,
5020 output: parser.File{
5021 Error: parser.ParseError{
5022 Err: errors.New("groups key must be a string, got a integer"),
5023 Line: 2,
5024 },
5025 },
5026 },
5027 {
5028 input: []byte(`
5029true: 0
5030`),
5031 strict: true,
5032 output: parser.File{
5033 Error: parser.ParseError{
5034 Err: errors.New("groups key must be a string, got a bool"),
5035 Line: 2,
5036 },
5037 },
5038 },
5039 {
5040 input: []byte(`
5041groups: !!binary "SGVsbG8sIFdvcmxkIQ=="
5042`),
5043 strict: true,
5044 output: parser.File{
5045 Error: parser.ParseError{
5046 Err: errors.New("groups value must be a list, got binary data"),
5047 Line: 2,
5048 },
5049 },
5050 },
5051 {
5052 input: []byte(`
5053groups:
5054 - true: null"
5055`),
5056 strict: true,
5057 output: parser.File{
5058 Groups: []parser.Group{
5059 {
5060 Error: parser.ParseError{
5061 Err: errors.New("invalid group key true"),
5062 Line: 3,
5063 },
5064 },
5065 },
5066 },
5067 },
5068 {
5069 input: []byte(`
5070!!!binary "groups": true"
5071`),
5072 strict: true,
5073 output: parser.File{
5074 Error: parser.ParseError{
5075 Err: errors.New("groups key must be a string, got a binary"),
5076 Line: 2,
5077 },
5078 },
5079 },
5080 {
5081 input: []byte("[]"),
5082 strict: true,
5083 output: parser.File{
5084 Error: parser.ParseError{
5085 Err: errors.New("top level field must be a groups key, got list"),
5086 Line: 1,
5087 },
5088 },
5089 },
5090 {
5091 input: []byte(`
5092groups:
5093 - true
5094`),
5095 strict: true,
5096 output: parser.File{
5097 Groups: []parser.Group{
5098 {
5099 Error: parser.ParseError{
5100 Err: errors.New("group must be a mapping, got bool"),
5101 Line: 3,
5102 },
5103 },
5104 },
5105 },
5106 },
5107 {
5108 input: []byte(`
5109groups:
5110 - name:
5111 rules: []
5112`),
5113 strict: true,
5114 output: parser.File{
5115 Groups: []parser.Group{
5116 {
5117 Error: parser.ParseError{
5118 Err: errors.New("group name must be a string, got null"),
5119 Line: 3,
5120 },
5121 },
5122 },
5123 },
5124 },
5125 {
5126 input: []byte(`
5127groups:
5128 - name: ""
5129 rules: []
5130`),
5131 strict: true,
5132 output: parser.File{
5133 Groups: []parser.Group{
5134 {
5135 Error: parser.ParseError{
5136 Err: errors.New("group name cannot be empty"),
5137 Line: 3,
5138 },
5139 },
5140 },
5141 },
5142 },
5143 {
5144 input: []byte(`
5145groups:
5146 - name: 1
5147 rules: []
5148`),
5149 strict: true,
5150 output: parser.File{
5151 Groups: []parser.Group{
5152 {
5153 Error: parser.ParseError{
5154 Err: errors.New("group name must be a string, got integer"),
5155 Line: 3,
5156 },
5157 },
5158 },
5159 },
5160 },
5161 {
5162 input: []byte(`
5163groups:
5164 - name: foo
5165 interval: 1
5166 rules: []
5167`),
5168 strict: true,
5169 output: parser.File{
5170 Groups: []parser.Group{
5171 {
5172 Name: parser.YamlNode{
5173 Value: "foo",
5174 Pos: diags.PositionRanges{
5175 {Line: 3, FirstColumn: 11, LastColumn: 13},
5176 },
5177 },
5178 Error: parser.ParseError{
5179 Err: errors.New("group interval must be a string, got integer"),
5180 Line: 4,
5181 },
5182 },
5183 },
5184 },
5185 },
5186 {
5187 input: []byte(`
5188groups:
5189 - name: foo
5190 interval: xxx
5191 rules: []
5192`),
5193 strict: true,
5194 output: parser.File{
5195 Groups: []parser.Group{
5196 {
5197 Name: parser.YamlNode{
5198 Value: "foo",
5199 Pos: diags.PositionRanges{
5200 {Line: 3, FirstColumn: 11, LastColumn: 13},
5201 },
5202 },
5203 Interval: &parser.YamlDuration{
5204 ParseError: errors.New(`not a valid duration string: "xxx"`),
5205 Raw: "xxx",
5206 Pos: diags.PositionRanges{
5207 {Line: 4, FirstColumn: 15, LastColumn: 17},
5208 },
5209 },
5210 Error: parser.ParseError{
5211 Err: errors.New("invalid interval value: not a valid duration string: \"xxx\""),
5212 Line: 4,
5213 },
5214 },
5215 },
5216 },
5217 },
5218 {
5219 input: []byte(`
5220groups:
5221 - name: foo
5222 query_offset: 1
5223 rules: []
5224`),
5225 strict: true,
5226 output: parser.File{
5227 Groups: []parser.Group{
5228 {
5229 Name: parser.YamlNode{
5230 Value: "foo",
5231 Pos: diags.PositionRanges{
5232 {Line: 3, FirstColumn: 11, LastColumn: 13},
5233 },
5234 },
5235 Error: parser.ParseError{
5236 Err: errors.New("group query_offset must be a string, got integer"),
5237 Line: 4,
5238 },
5239 },
5240 },
5241 },
5242 },
5243 {
5244 input: []byte(`
5245groups:
5246 - name: foo
5247 query_offset: xxx
5248 rules: []
5249`),
5250 strict: true,
5251 output: parser.File{
5252 Groups: []parser.Group{
5253 {
5254 Name: parser.YamlNode{
5255 Value: "foo",
5256 Pos: diags.PositionRanges{
5257 {Line: 3, FirstColumn: 11, LastColumn: 13},
5258 },
5259 },
5260 QueryOffset: &parser.YamlDuration{
5261 ParseError: errors.New(`not a valid duration string: "xxx"`),
5262 Raw: "xxx",
5263 Pos: diags.PositionRanges{
5264 {Line: 4, FirstColumn: 19, LastColumn: 21},
5265 },
5266 },
5267 Error: parser.ParseError{
5268 Err: errors.New("invalid query_offset value: not a valid duration string: \"xxx\""),
5269 Line: 4,
5270 },
5271 },
5272 },
5273 },
5274 },
5275 {
5276 input: []byte(`
5277groups:
5278 - name: foo
5279 query_offset: 1m
5280 limit: abc
5281 rules: []
5282`),
5283 strict: true,
5284 output: parser.File{
5285 Groups: []parser.Group{
5286 {
5287 Name: parser.YamlNode{
5288 Value: "foo",
5289 Pos: diags.PositionRanges{
5290 {Line: 3, FirstColumn: 11, LastColumn: 13},
5291 },
5292 },
5293 QueryOffset: &parser.YamlDuration{
5294 Value: time.Minute,
5295 Raw: "1m",
5296 Pos: diags.PositionRanges{
5297 {Line: 4, FirstColumn: 19, LastColumn: 20},
5298 },
5299 },
5300 Error: parser.ParseError{
5301 Err: errors.New("group limit must be a integer, got string"),
5302 Line: 5,
5303 },
5304 },
5305 },
5306 },
5307 },
5308 {
5309 input: []byte(`
5310groups:
5311- name: v2
5312 rules:
5313 - alert: up:count
5314 for: 5m &timeout
5315 keep_firing_for: **timeout
5316 expr: count(up)
5317 labels: {}
5318 annotations: {}
5319 bogus: 1
5320`),
5321 strict: true,
5322 output: parser.File{
5323 Error: parser.ParseError{
5324 Err: errors.New("did not find expected alphabetic or numeric character"),
5325 Line: 7,
5326 },
5327 },
5328 },
5329 {
5330 // Non-strict parser accepts any scalar value for limit and records
5331 // the strconv error on the YamlInt itself.
5332 input: []byte(`
5333groups:
5334- name: foo
5335 limit: abc
5336 rules: []
5337`),
5338 output: parser.File{
5339 IsRelaxed: true,
5340 Groups: []parser.Group{
5341 {
5342 Name: parser.YamlNode{
5343 Value: "foo",
5344 Pos: diags.PositionRanges{
5345 {Line: 3, FirstColumn: 9, LastColumn: 11},
5346 },
5347 },
5348 Limit: &parser.YamlInt{
5349 ParseError: errors.New(`strconv.Atoi: parsing "abc": invalid syntax`),
5350 Raw: "abc",
5351 Pos: diags.PositionRanges{
5352 {Line: 4, FirstColumn: 10, LastColumn: 12},
5353 },
5354 },
5355 },
5356 },
5357 },
5358 },
5359 {
5360 input: []byte(`
5361groups:
5362- name: v2
5363 rules:
5364 - alert: up:count
5365 for: &for 1
5366 keep_firing_for: *for
5367 expr: count(up)
5368 labels: {}
5369 annotations: {}
5370`),
5371 strict: true,
5372 output: parser.File{
5373 Groups: []parser.Group{
5374 {
5375 Name: parser.YamlNode{
5376 Value: "v2",
5377 Pos: diags.PositionRanges{
5378 {Line: 3, FirstColumn: 9, LastColumn: 10},
5379 },
5380 },
5381 Rules: []parser.Rule{
5382 {
5383 Lines: diags.LineRange{First: 5, Last: 10},
5384 Error: parser.ParseError{
5385 Line: 6,
5386 Err: errors.New("for value must be a string, got integer instead"),
5387 },
5388 },
5389 },
5390 },
5391 },
5392 },
5393 },
5394 {
5395 input: []byte(`
5396groups:
5397- name: v2
5398 limit: &for 1
5399 rules:
5400 - alert: up:count
5401 keep_firing_for: *for
5402 expr: count(up)
5403 labels: {}
5404 annotations: {}
5405`),
5406 strict: true,
5407 output: parser.File{
5408 Groups: []parser.Group{
5409 {
5410 Name: parser.YamlNode{
5411 Value: "v2",
5412 Pos: diags.PositionRanges{
5413 {Line: 3, FirstColumn: 9, LastColumn: 10},
5414 },
5415 },
5416 Limit: &parser.YamlInt{
5417 Value: 1,
5418 Raw: "1",
5419 Pos: diags.PositionRanges{
5420 {Line: 4, FirstColumn: 15, LastColumn: 15},
5421 },
5422 },
5423 Rules: []parser.Rule{
5424 {
5425 Lines: diags.LineRange{First: 6, Last: 10},
5426 Error: parser.ParseError{
5427 Line: 7,
5428 Err: errors.New("keep_firing_for value must be a string, got integer instead"),
5429 },
5430 },
5431 },
5432 },
5433 },
5434 },
5435 },
5436 {
5437 input: []byte(`
5438groups:
5439- name: "{{ source }}"
5440 rules:
5441# pint ignore/begin
5442
5443# pint ignore/end
5444`),
5445 strict: true,
5446 output: parser.File{
5447 Groups: []parser.Group{
5448 {
5449 Name: parser.YamlNode{
5450 Value: "{{ source }}",
5451 Pos: diags.PositionRanges{
5452 {Line: 3, FirstColumn: 10, LastColumn: 21},
5453 },
5454 },
5455 },
5456 },
5457 },
5458 },
5459 {
5460 input: []byte(`
5461groups:
5462- name: foo
5463 rules:
5464 - record: foo
5465 expr: |
5466 {"up"}
5467`),
5468 output: parser.File{
5469 IsRelaxed: true,
5470 Groups: []parser.Group{
5471 {
5472 Name: parser.YamlNode{
5473 Value: "foo",
5474 Pos: diags.PositionRanges{
5475 {Line: 3, FirstColumn: 9, LastColumn: 11},
5476 },
5477 },
5478 Rules: []parser.Rule{
5479 {
5480 Lines: diags.LineRange{First: 5, Last: 7},
5481 RecordingRule: &parser.RecordingRule{
5482 Record: parser.YamlNode{
5483 Value: "foo",
5484 Pos: diags.PositionRanges{
5485 {Line: 5, FirstColumn: 13, LastColumn: 15},
5486 },
5487 },
5488 Expr: parser.PromQLExpr{
5489 Value: &parser.YamlNode{
5490 Value: "{\"up\"}\n",
5491 Pos: diags.PositionRanges{
5492 {Line: 7, FirstColumn: 7, LastColumn: 12},
5493 },
5494 },
5495 },
5496 },
5497 },
5498 },
5499 },
5500 },
5501 },
5502 },
5503 {
5504 input: []byte(`
5505groups:
5506- name: foo
5507 rules:
5508 - record: foo
5509 expr: |
5510 {'up'}
5511`),
5512 output: parser.File{
5513 IsRelaxed: true,
5514 Groups: []parser.Group{
5515 {
5516 Name: parser.YamlNode{
5517 Value: "foo",
5518 Pos: diags.PositionRanges{
5519 {Line: 3, FirstColumn: 9, LastColumn: 11},
5520 },
5521 },
5522 Rules: []parser.Rule{
5523 {
5524 Lines: diags.LineRange{First: 5, Last: 7},
5525 RecordingRule: &parser.RecordingRule{
5526 Record: parser.YamlNode{
5527 Value: "foo",
5528 Pos: diags.PositionRanges{
5529 {Line: 5, FirstColumn: 13, LastColumn: 15},
5530 },
5531 },
5532 Expr: parser.PromQLExpr{
5533 Value: &parser.YamlNode{
5534 Value: "{'up'}\n",
5535 Pos: diags.PositionRanges{
5536 {Line: 7, FirstColumn: 7, LastColumn: 12},
5537 },
5538 },
5539 },
5540 },
5541 },
5542 },
5543 },
5544 },
5545 },
5546 },
5547 {
5548 input: []byte(`
5549groups:
5550- name: mygroup
5551 partial_response_strategy: bob
5552 rules:
5553 - record: up:count
5554 expr: count(up)
5555`),
5556 strict: true,
5557 output: parser.File{
5558 Groups: []parser.Group{
5559 {
5560 Name: parser.YamlNode{
5561 Value: "mygroup",
5562 Pos: diags.PositionRanges{
5563 {Line: 3, FirstColumn: 9, LastColumn: 15},
5564 },
5565 },
5566 Error: parser.ParseError{
5567 Err: errors.New("partial_response_strategy is only valid when parser is configured to use the Thanos rule schema"),
5568 Line: 4,
5569 },
5570 },
5571 },
5572 },
5573 },
5574 {
5575 input: []byte(`
5576groups:
5577- name: mygroup
5578 partial_response_strategy: warn
5579 rules:
5580 - record: up:count
5581 expr: count(up)
5582`),
5583 strict: true,
5584 schema: parser.ThanosSchema,
5585 output: parser.File{
5586 Groups: []parser.Group{
5587 {
5588 Name: parser.YamlNode{
5589 Value: "mygroup",
5590 Pos: diags.PositionRanges{
5591 {Line: 3, FirstColumn: 9, LastColumn: 15},
5592 },
5593 },
5594 Rules: []parser.Rule{
5595 {
5596 Lines: diags.LineRange{First: 6, Last: 7},
5597 RecordingRule: &parser.RecordingRule{
5598 Record: parser.YamlNode{
5599 Value: "up:count",
5600 Pos: diags.PositionRanges{
5601 {Line: 6, FirstColumn: 13, LastColumn: 20},
5602 },
5603 },
5604 Expr: parser.PromQLExpr{
5605 Value: &parser.YamlNode{
5606 Value: "count(up)",
5607 Pos: diags.PositionRanges{
5608 {Line: 7, FirstColumn: 11, LastColumn: 19},
5609 },
5610 },
5611 },
5612 },
5613 },
5614 },
5615 },
5616 },
5617 },
5618 },
5619 {
5620 input: []byte(`
5621groups:
5622- name: mygroup
5623 partial_response_strategy: abort
5624 rules:
5625 - record: up:count
5626 expr: count(up)
5627`),
5628 strict: true,
5629 schema: parser.ThanosSchema,
5630 output: parser.File{
5631 Groups: []parser.Group{
5632 {
5633 Name: parser.YamlNode{
5634 Value: "mygroup",
5635 Pos: diags.PositionRanges{
5636 {Line: 3, FirstColumn: 9, LastColumn: 15},
5637 },
5638 },
5639 Rules: []parser.Rule{
5640 {
5641 Lines: diags.LineRange{First: 6, Last: 7},
5642 RecordingRule: &parser.RecordingRule{
5643 Record: parser.YamlNode{
5644 Value: "up:count",
5645 Pos: diags.PositionRanges{
5646 {Line: 6, FirstColumn: 13, LastColumn: 20},
5647 },
5648 },
5649 Expr: parser.PromQLExpr{
5650 Value: &parser.YamlNode{
5651 Value: "count(up)",
5652 Pos: diags.PositionRanges{
5653 {Line: 7, FirstColumn: 11, LastColumn: 19},
5654 },
5655 },
5656 },
5657 },
5658 },
5659 },
5660 },
5661 },
5662 },
5663 },
5664 {
5665 input: []byte(`
5666groups:
5667- name: mygroup
5668 partial_response_strategy: abort
5669 rules:
5670 - record: up:count
5671 expr: count(up)
5672`),
5673 strict: false,
5674 schema: parser.PrometheusSchema,
5675 output: parser.File{
5676 IsRelaxed: true,
5677 Groups: []parser.Group{
5678 {
5679 Name: parser.YamlNode{
5680 Value: "mygroup",
5681 Pos: diags.PositionRanges{
5682 {Line: 3, FirstColumn: 9, LastColumn: 15},
5683 },
5684 },
5685 Rules: []parser.Rule{
5686 {
5687 Lines: diags.LineRange{First: 6, Last: 7},
5688 RecordingRule: &parser.RecordingRule{
5689 Record: parser.YamlNode{
5690 Value: "up:count",
5691 Pos: diags.PositionRanges{
5692 {Line: 6, FirstColumn: 13, LastColumn: 20},
5693 },
5694 },
5695 Expr: parser.PromQLExpr{
5696 Value: &parser.YamlNode{
5697 Value: "count(up)",
5698 Pos: diags.PositionRanges{
5699 {Line: 7, FirstColumn: 11, LastColumn: 19},
5700 },
5701 },
5702 },
5703 },
5704 },
5705 },
5706 },
5707 },
5708 },
5709 },
5710 {
5711 input: []byte(`
5712groups:
5713- name: mygroup
5714 partial_response_strategy: bob
5715 rules:
5716 - record: up:count
5717 expr: count(up)
5718`),
5719 strict: true,
5720 schema: parser.ThanosSchema,
5721 output: parser.File{
5722 Groups: []parser.Group{
5723 {
5724 Name: parser.YamlNode{
5725 Value: "mygroup",
5726 Pos: diags.PositionRanges{
5727 {Line: 3, FirstColumn: 9, LastColumn: 15},
5728 },
5729 },
5730 Error: parser.ParseError{
5731 Err: errors.New("invalid partial_response_strategy value: bob"),
5732 Line: 4,
5733 },
5734 },
5735 },
5736 },
5737 },
5738 {
5739 // YAML alias on partial_response_strategy in Thanos strict mode is resolved correctly
5740 input: []byte(`
5741groups:
5742- name: group1
5743 partial_response_strategy: &prs warn
5744 rules:
5745 - record: up:count
5746 expr: count(up)
5747- name: group2
5748 partial_response_strategy: *prs
5749 rules:
5750 - record: down:count
5751 expr: count(down)
5752`),
5753 strict: true,
5754 schema: parser.ThanosSchema,
5755 output: parser.File{
5756 Groups: []parser.Group{
5757 {
5758 Name: parser.YamlNode{
5759 Value: "group1",
5760 Pos: diags.PositionRanges{
5761 {Line: 3, FirstColumn: 9, LastColumn: 14},
5762 },
5763 },
5764 Rules: []parser.Rule{
5765 {
5766 Lines: diags.LineRange{First: 6, Last: 7},
5767 RecordingRule: &parser.RecordingRule{
5768 Record: parser.YamlNode{
5769 Value: "up:count",
5770 Pos: diags.PositionRanges{
5771 {Line: 6, FirstColumn: 13, LastColumn: 20},
5772 },
5773 },
5774 Expr: parser.PromQLExpr{
5775 Value: &parser.YamlNode{
5776 Value: "count(up)",
5777 Pos: diags.PositionRanges{
5778 {Line: 7, FirstColumn: 11, LastColumn: 19},
5779 },
5780 },
5781 },
5782 },
5783 },
5784 },
5785 },
5786 {
5787 Name: parser.YamlNode{
5788 Value: "group2",
5789 Pos: diags.PositionRanges{
5790 {Line: 8, FirstColumn: 9, LastColumn: 14},
5791 },
5792 },
5793 Rules: []parser.Rule{
5794 {
5795 Lines: diags.LineRange{First: 11, Last: 12},
5796 RecordingRule: &parser.RecordingRule{
5797 Record: parser.YamlNode{
5798 Value: "down:count",
5799 Pos: diags.PositionRanges{
5800 {Line: 11, FirstColumn: 13, LastColumn: 22},
5801 },
5802 },
5803 Expr: parser.PromQLExpr{
5804 Value: &parser.YamlNode{
5805 Value: "count(down)",
5806 Pos: diags.PositionRanges{
5807 {Line: 12, FirstColumn: 11, LastColumn: 21},
5808 },
5809 },
5810 },
5811 },
5812 },
5813 },
5814 },
5815 },
5816 },
5817 },
5818 {
5819 input: []byte(`
5820groups:
5821- name: mygroup
5822 partial_response_strategy: 1
5823 rules:
5824 - record: up:count
5825 expr: count(up)
5826`),
5827 strict: true,
5828 schema: parser.ThanosSchema,
5829 output: parser.File{
5830 Groups: []parser.Group{
5831 {
5832 Name: parser.YamlNode{
5833 Value: "mygroup",
5834 Pos: diags.PositionRanges{
5835 {Line: 3, FirstColumn: 9, LastColumn: 15},
5836 },
5837 },
5838 Error: parser.ParseError{
5839 Err: errors.New("partial_response_strategy must be a string, got integer"),
5840 Line: 4,
5841 },
5842 },
5843 },
5844 },
5845 },
5846 {
5847 input: []byte(`
5848- alert: Multi Line
5849 expr: foo
5850 AND ON (instance)
5851 bar
5852`),
5853 strict: false,
5854 schema: parser.PrometheusSchema,
5855 output: parser.File{
5856 IsRelaxed: true,
5857 Groups: []parser.Group{
5858 {
5859 Rules: []parser.Rule{
5860 {
5861 Lines: diags.LineRange{First: 2, Last: 5},
5862 AlertingRule: &parser.AlertingRule{
5863 Alert: parser.YamlNode{
5864 Value: "Multi Line",
5865 Pos: diags.PositionRanges{
5866 {Line: 2, FirstColumn: 10, LastColumn: 19},
5867 },
5868 },
5869 Expr: parser.PromQLExpr{
5870 Value: &parser.YamlNode{
5871 Value: "foo AND ON (instance) bar",
5872 Pos: diags.PositionRanges{
5873 {Line: 3, FirstColumn: 9, LastColumn: 12},
5874 {Line: 4, FirstColumn: 11, LastColumn: 28},
5875 {Line: 5, FirstColumn: 11, LastColumn: 13},
5876 },
5877 },
5878 },
5879 },
5880 },
5881 },
5882 },
5883 },
5884 },
5885 },
5886 {
5887 input: []byte(`
5888 - alert: FooBar
5889 expr: >-
5890 count(
5891 foo
5892 or
5893 bar
5894 ) > 0
5895`),
5896 strict: false,
5897 schema: parser.PrometheusSchema,
5898 output: parser.File{
5899 IsRelaxed: true,
5900 Groups: []parser.Group{
5901 {
5902 Rules: []parser.Rule{
5903 {
5904 Lines: diags.LineRange{First: 2, Last: 8},
5905 AlertingRule: &parser.AlertingRule{
5906 Alert: parser.YamlNode{
5907 Value: "FooBar",
5908 Pos: diags.PositionRanges{
5909 {Line: 2, FirstColumn: 12, LastColumn: 17},
5910 },
5911 },
5912 Expr: parser.PromQLExpr{
5913 Value: &parser.YamlNode{
5914 Value: "count(\n foo\n or\n bar\n) > 0",
5915 Pos: diags.PositionRanges{
5916 {Line: 4, FirstColumn: 7, LastColumn: 13},
5917 {Line: 5, FirstColumn: 7, LastColumn: 12},
5918 {Line: 6, FirstColumn: 7, LastColumn: 11},
5919 {Line: 7, FirstColumn: 7, LastColumn: 12},
5920 {Line: 8, FirstColumn: 7, LastColumn: 11},
5921 },
5922 },
5923 },
5924 },
5925 },
5926 },
5927 },
5928 },
5929 },
5930 },
5931 {
5932 input: []byte(`
5933 - alert: FooBar
5934 expr: >-
5935 aaaaaaaaaaaaaaaaaaaaaaaa
5936 AND ON (colo_id) bbbbbbbbbbb
5937 > 2
5938 for: 1m
5939`),
5940 strict: false,
5941 schema: parser.PrometheusSchema,
5942 output: parser.File{
5943 IsRelaxed: true,
5944 Groups: []parser.Group{
5945 {
5946 Rules: []parser.Rule{
5947 {
5948 Lines: diags.LineRange{First: 2, Last: 7},
5949 AlertingRule: &parser.AlertingRule{
5950 Alert: parser.YamlNode{
5951 Value: "FooBar",
5952 Pos: diags.PositionRanges{
5953 {Line: 2, FirstColumn: 12, LastColumn: 17},
5954 },
5955 },
5956 Expr: parser.PromQLExpr{
5957 Value: &parser.YamlNode{
5958 Value: "aaaaaaaaaaaaaaaaaaaaaaaa AND ON (colo_id) bbbbbbbbbbb > 2",
5959 Pos: diags.PositionRanges{
5960 {Line: 4, FirstColumn: 7, LastColumn: 31},
5961 {Line: 5, FirstColumn: 7, LastColumn: 35},
5962 {Line: 6, FirstColumn: 7, LastColumn: 9},
5963 },
5964 },
5965 },
5966 For: &parser.YamlDuration{
5967 Value: time.Minute,
5968 Raw: "1m",
5969 Pos: diags.PositionRanges{
5970 {Line: 7, FirstColumn: 10, LastColumn: 11},
5971 },
5972 },
5973 },
5974 },
5975 },
5976 },
5977 },
5978 },
5979 },
5980 {
5981 input: []byte(`
5982 - alert: FooBar
5983 expr: 'aaaaaaaaaaaaaaaaaaaaaaaa
5984 AND ON (colo_id) bbbbbbbbbbb
5985 > 2'
5986 for: 1m
5987`),
5988 strict: false,
5989 schema: parser.PrometheusSchema,
5990 output: parser.File{
5991 IsRelaxed: true,
5992 Groups: []parser.Group{
5993 {
5994 Rules: []parser.Rule{
5995 {
5996 Lines: diags.LineRange{First: 2, Last: 6},
5997 AlertingRule: &parser.AlertingRule{
5998 Alert: parser.YamlNode{
5999 Value: "FooBar",
6000 Pos: diags.PositionRanges{
6001 {Line: 2, FirstColumn: 12, LastColumn: 17},
6002 },
6003 },
6004 Expr: parser.PromQLExpr{
6005 Value: &parser.YamlNode{
6006 Value: "aaaaaaaaaaaaaaaaaaaaaaaa AND ON (colo_id) bbbbbbbbbbb > 2",
6007 Pos: diags.PositionRanges{
6008 {Line: 3, FirstColumn: 12, LastColumn: 36},
6009 {Line: 4, FirstColumn: 11, LastColumn: 39},
6010 {Line: 5, FirstColumn: 11, LastColumn: 13},
6011 },
6012 },
6013 },
6014 For: &parser.YamlDuration{
6015 Value: time.Minute,
6016 Raw: "1m",
6017 Pos: diags.PositionRanges{
6018 {Line: 6, FirstColumn: 10, LastColumn: 11},
6019 },
6020 },
6021 },
6022 },
6023 },
6024 },
6025 },
6026 },
6027 },
6028 {
6029 input: []byte(`
6030groups:
6031- name: foo
6032 rules:
6033 - record: colo:foo:sum
6034 expr: sum without (instance) ( rate(my_metric[2m]) * on (instance)
6035 group_left (hardware_generation, hms_scope, sliver) (instance:metadata{})
6036 )
6037`),
6038 strict: false,
6039 schema: parser.PrometheusSchema,
6040 output: parser.File{
6041 IsRelaxed: true,
6042 Groups: []parser.Group{
6043 {
6044 Name: parser.YamlNode{
6045 Value: "foo",
6046 Pos: diags.PositionRanges{
6047 {Line: 3, FirstColumn: 9, LastColumn: 11},
6048 },
6049 },
6050 Rules: []parser.Rule{
6051 {
6052 Lines: diags.LineRange{First: 5, Last: 8},
6053 RecordingRule: &parser.RecordingRule{
6054 Record: parser.YamlNode{
6055 Value: "colo:foo:sum",
6056 Pos: diags.PositionRanges{
6057 {Line: 5, FirstColumn: 13, LastColumn: 24},
6058 },
6059 },
6060 Expr: parser.PromQLExpr{
6061 Value: &parser.YamlNode{
6062 Value: "sum without (instance) ( rate(my_metric[2m]) * on (instance) group_left (hardware_generation, hms_scope, sliver) (instance:metadata{}) )",
6063 Pos: diags.PositionRanges{
6064 {Line: 6, FirstColumn: 11, LastColumn: 71},
6065 {Line: 7, FirstColumn: 7, LastColumn: 80},
6066 {Line: 8, FirstColumn: 7, LastColumn: 7},
6067 },
6068 },
6069 },
6070 },
6071 },
6072 },
6073 },
6074 },
6075 },
6076 },
6077 {
6078 input: []byte(`
6079groups:
6080- name: "{{ source }}"
6081 rules:
6082 # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
6083 # BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
6084 - record: some_long_name
6085 labels:
6086 metricssource: receiver
6087 expr:
6088 clamp_max(
6089 sum(rate(my_metric_long_name_total{output="control:output:kafka:/:requests:http-b:http_requests_control_sample"}[5m])) /
6090 sum(rate(my_metric_long_name_total{output="control:output:kafka:/:requests:http-b:http_requests_control_sample"}[5m] offset 30m)),
6091 2
6092 )
6093`),
6094 strict: true,
6095 schema: parser.PrometheusSchema,
6096 output: parser.File{
6097 Groups: []parser.Group{
6098 {
6099 Name: parser.YamlNode{
6100 Value: "{{ source }}",
6101 Pos: diags.PositionRanges{
6102 {Line: 3, FirstColumn: 10, LastColumn: 21},
6103 },
6104 },
6105 Rules: []parser.Rule{
6106 {
6107 Lines: diags.LineRange{First: 7, Last: 15},
6108 RecordingRule: &parser.RecordingRule{
6109 Record: parser.YamlNode{
6110 Value: "some_long_name",
6111 Pos: diags.PositionRanges{
6112 {Line: 7, FirstColumn: 13, LastColumn: 26},
6113 },
6114 },
6115 Labels: &parser.YamlMap{
6116 Key: &parser.YamlNode{
6117 Value: "labels",
6118 Pos: diags.PositionRanges{
6119 {Line: 8, FirstColumn: 5, LastColumn: 10},
6120 },
6121 },
6122 Items: []*parser.YamlKeyValue{
6123 {
6124 Key: &parser.YamlNode{
6125 Value: "metricssource",
6126 Pos: diags.PositionRanges{
6127 {Line: 9, FirstColumn: 7, LastColumn: 19},
6128 },
6129 },
6130 Value: &parser.YamlNode{
6131 Value: "receiver",
6132 Pos: diags.PositionRanges{
6133 {Line: 9, FirstColumn: 22, LastColumn: 29},
6134 },
6135 },
6136 },
6137 },
6138 },
6139 Expr: parser.PromQLExpr{
6140 Value: &parser.YamlNode{
6141 Value: `clamp_max( sum(rate(my_metric_long_name_total{output="control:output:kafka:/:requests:http-b:http_requests_control_sample"}[5m])) / sum(rate(my_metric_long_name_total{output="control:output:kafka:/:requests:http-b:http_requests_control_sample"}[5m] offset 30m)), 2 )`,
6142 Pos: diags.PositionRanges{
6143 {Line: 11, FirstColumn: 7, LastColumn: 17},
6144 {Line: 12, FirstColumn: 9, LastColumn: 129},
6145 {Line: 13, FirstColumn: 9, LastColumn: 139},
6146 {Line: 14, FirstColumn: 9, LastColumn: 10},
6147 {Line: 15, FirstColumn: 7, LastColumn: 7},
6148 },
6149 },
6150 },
6151 },
6152 },
6153 },
6154 },
6155 },
6156 },
6157 },
6158 {
6159 input: []byte(`
6160groups:
6161- name: "{{ source }}"
6162 rules:
6163
6164 - alert: Director_Is_Not_Advertising_Any_Routes
6165 expr: |
6166 sum without (name) (
6167 bird_protocol_prefix_export_count{ip_version="4",name=~".*external.*",proto!="Kernel"}
6168 * on (instance) group_left (profile,cluster)
6169 cf_node_role{kubernetes_role="director",role="kubernetes"}
6170 ) <= 0
6171 for: 1m
6172`),
6173 strict: true,
6174 schema: parser.PrometheusSchema,
6175 output: parser.File{
6176 Groups: []parser.Group{
6177 {
6178 Name: parser.YamlNode{
6179 Value: "{{ source }}",
6180 Pos: diags.PositionRanges{
6181 {Line: 3, FirstColumn: 10, LastColumn: 21},
6182 },
6183 },
6184 Rules: []parser.Rule{
6185 {
6186 Lines: diags.LineRange{First: 6, Last: 13},
6187 AlertingRule: &parser.AlertingRule{
6188 Alert: parser.YamlNode{
6189 Value: "Director_Is_Not_Advertising_Any_Routes",
6190 Pos: diags.PositionRanges{
6191 {Line: 6, FirstColumn: 12, LastColumn: 49},
6192 },
6193 },
6194 Expr: parser.PromQLExpr{
6195 Value: &parser.YamlNode{
6196 Value: `sum without (name) (
6197 bird_protocol_prefix_export_count{ip_version="4",name=~".*external.*",proto!="Kernel"}
6198 * on (instance) group_left (profile,cluster)
6199 cf_node_role{kubernetes_role="director",role="kubernetes"}
6200) <= 0
6201`,
6202 Pos: diags.PositionRanges{
6203 {Line: 8, FirstColumn: 9, LastColumn: 29},
6204 {Line: 9, FirstColumn: 9, LastColumn: 99},
6205 {Line: 10, FirstColumn: 9, LastColumn: 55},
6206 {Line: 11, FirstColumn: 9, LastColumn: 71},
6207 {Line: 12, FirstColumn: 9, LastColumn: 14},
6208 },
6209 },
6210 },
6211 For: &parser.YamlDuration{
6212 Value: time.Minute,
6213 Raw: "1m",
6214 Pos: diags.PositionRanges{
6215 {Line: 13, FirstColumn: 10, LastColumn: 11},
6216 },
6217 },
6218 },
6219 },
6220 },
6221 },
6222 },
6223 },
6224 },
6225 {
6226 input: []byte(`
6227groups:
6228- name: xxx
6229 interval: 3m
6230 rules: []
6231`),
6232 strict: true,
6233 output: parser.File{
6234 Groups: []parser.Group{
6235 {
6236 Name: parser.YamlNode{
6237 Value: "xxx",
6238 Pos: diags.PositionRanges{
6239 {Line: 3, FirstColumn: 9, LastColumn: 11},
6240 },
6241 },
6242 Interval: &parser.YamlDuration{
6243 Value: time.Minute * 3,
6244 Raw: "3m",
6245 Pos: diags.PositionRanges{
6246 {Line: 4, FirstColumn: 13, LastColumn: 14},
6247 },
6248 },
6249 },
6250 },
6251 },
6252 },
6253 {
6254 input: []byte(`
6255groups:
6256- name: xxx
6257 interval: 3m
6258 rules: []
6259`),
6260 output: parser.File{
6261 IsRelaxed: true,
6262 Groups: []parser.Group{
6263 {
6264 Name: parser.YamlNode{
6265 Value: "xxx",
6266 Pos: diags.PositionRanges{
6267 {Line: 3, FirstColumn: 9, LastColumn: 11},
6268 },
6269 },
6270 Interval: &parser.YamlDuration{
6271 Value: time.Minute * 3,
6272 Raw: "3m",
6273 Pos: diags.PositionRanges{
6274 {Line: 4, FirstColumn: 13, LastColumn: 14},
6275 },
6276 },
6277 },
6278 },
6279 },
6280 },
6281 {
6282 input: []byte(`
6283groups:
6284- name: xxx
6285 interval: 3m
6286 rules: []
6287---
6288groups:
6289- name: yyy
6290 interval: 2m
6291 rules: []
6292`),
6293 output: parser.File{
6294 IsRelaxed: true,
6295 Groups: []parser.Group{
6296 {
6297 Name: parser.YamlNode{
6298 Value: "xxx",
6299 Pos: diags.PositionRanges{
6300 {Line: 3, FirstColumn: 9, LastColumn: 11},
6301 },
6302 },
6303 Interval: &parser.YamlDuration{
6304 Value: time.Minute * 3,
6305 Raw: "3m",
6306 Pos: diags.PositionRanges{
6307 {Line: 4, FirstColumn: 13, LastColumn: 14},
6308 },
6309 },
6310 },
6311 {
6312 Name: parser.YamlNode{
6313 Value: "yyy",
6314 Pos: diags.PositionRanges{
6315 {Line: 8, FirstColumn: 9, LastColumn: 11},
6316 },
6317 },
6318 Interval: &parser.YamlDuration{
6319 Value: time.Minute * 2,
6320 Raw: "2m",
6321 Pos: diags.PositionRanges{
6322 {Line: 9, FirstColumn: 13, LastColumn: 14},
6323 },
6324 },
6325 },
6326 },
6327 },
6328 },
6329 {
6330 input: []byte(`
6331groups:
6332- name: xxx
6333 interval: 3m
6334 labels:
6335 foo: bar
6336 rules: []
6337`),
6338 strict: true,
6339 output: parser.File{
6340 Groups: []parser.Group{
6341 {
6342 Name: parser.YamlNode{
6343 Value: "xxx",
6344 Pos: diags.PositionRanges{
6345 {Line: 3, FirstColumn: 9, LastColumn: 11},
6346 },
6347 },
6348 Interval: &parser.YamlDuration{
6349 Value: time.Minute * 3,
6350 Raw: "3m",
6351 Pos: diags.PositionRanges{
6352 {Line: 4, FirstColumn: 13, LastColumn: 14},
6353 },
6354 },
6355 Labels: &parser.YamlMap{
6356 Key: &parser.YamlNode{
6357 Value: "labels",
6358 Pos: diags.PositionRanges{
6359 {Line: 5, FirstColumn: 3, LastColumn: 8},
6360 },
6361 },
6362 Items: []*parser.YamlKeyValue{
6363 {
6364 Key: &parser.YamlNode{
6365 Value: "foo",
6366 Pos: diags.PositionRanges{
6367 {Line: 6, FirstColumn: 5, LastColumn: 7},
6368 },
6369 },
6370 Value: &parser.YamlNode{
6371 Value: "bar",
6372 Pos: diags.PositionRanges{
6373 {Line: 6, FirstColumn: 10, LastColumn: 12},
6374 },
6375 },
6376 },
6377 },
6378 },
6379 },
6380 },
6381 },
6382 },
6383 {
6384 input: []byte(`
6385groups:
6386- name: xxx
6387 labels:
6388 - foo: bar
6389 rules: []
6390`),
6391 strict: true,
6392 output: parser.File{
6393 Groups: []parser.Group{
6394 {
6395 Name: parser.YamlNode{
6396 Value: "xxx",
6397 Pos: diags.PositionRanges{
6398 {Line: 3, FirstColumn: 9, LastColumn: 11},
6399 },
6400 },
6401 Error: parser.ParseError{
6402 Err: errors.New("group labels must be a mapping, got list"),
6403 Line: 4,
6404 },
6405 },
6406 },
6407 },
6408 },
6409 {
6410 input: []byte(`
6411groups:
6412- name: xxx
6413 labels:
6414 foo: 1
6415 rules: []
6416`),
6417 strict: true,
6418 output: parser.File{
6419 Groups: []parser.Group{
6420 {
6421 Name: parser.YamlNode{
6422 Value: "xxx",
6423 Pos: diags.PositionRanges{
6424 {Line: 3, FirstColumn: 9, LastColumn: 11},
6425 },
6426 },
6427 Error: parser.ParseError{
6428 Err: errors.New("labels foo value must be a string, got integer instead"),
6429 Line: 5,
6430 },
6431 },
6432 },
6433 },
6434 },
6435 {
6436 input: []byte(`
6437groups:
6438- name: xxx
6439 labels:
6440 foo: bar
6441 bob: foo
6442 foo: bob
6443 rules: []
6444`),
6445 strict: true,
6446 output: parser.File{
6447 Groups: []parser.Group{
6448 {
6449 Name: parser.YamlNode{
6450 Value: "xxx",
6451 Pos: diags.PositionRanges{
6452 {Line: 3, FirstColumn: 9, LastColumn: 11},
6453 },
6454 },
6455 Error: parser.ParseError{
6456 Err: errors.New("duplicated labels key foo"),
6457 Line: 7,
6458 },
6459 },
6460 },
6461 },
6462 },
6463 {
6464 input: []byte(`
6465groups:
6466- name: xxx
6467 interval: 3m
6468 query_offset: 1s
6469 limit: 5
6470 labels:
6471 foo: bar
6472 rules: []
6473`),
6474 output: parser.File{
6475 IsRelaxed: true,
6476 Groups: []parser.Group{
6477 {
6478 Name: parser.YamlNode{
6479 Value: "xxx",
6480 Pos: diags.PositionRanges{
6481 {Line: 3, FirstColumn: 9, LastColumn: 11},
6482 },
6483 },
6484 Interval: &parser.YamlDuration{
6485 Value: time.Minute * 3,
6486 Raw: "3m",
6487 Pos: diags.PositionRanges{
6488 {Line: 4, FirstColumn: 13, LastColumn: 14},
6489 },
6490 },
6491 QueryOffset: &parser.YamlDuration{
6492 Value: time.Second,
6493 Raw: "1s",
6494 Pos: diags.PositionRanges{
6495 {Line: 5, FirstColumn: 17, LastColumn: 18},
6496 },
6497 },
6498 Limit: &parser.YamlInt{
6499 Value: 5,
6500 Raw: "5",
6501 Pos: diags.PositionRanges{
6502 {Line: 6, FirstColumn: 10, LastColumn: 10},
6503 },
6504 },
6505 Labels: &parser.YamlMap{
6506 Key: &parser.YamlNode{
6507 Value: "labels",
6508 Pos: diags.PositionRanges{
6509 {Line: 7, FirstColumn: 3, LastColumn: 8},
6510 },
6511 },
6512 Items: []*parser.YamlKeyValue{
6513 {
6514 Key: &parser.YamlNode{
6515 Value: "foo",
6516 Pos: diags.PositionRanges{
6517 {Line: 8, FirstColumn: 5, LastColumn: 7},
6518 },
6519 },
6520 Value: &parser.YamlNode{
6521 Value: "bar",
6522 Pos: diags.PositionRanges{
6523 {Line: 8, FirstColumn: 10, LastColumn: 12},
6524 },
6525 },
6526 },
6527 },
6528 },
6529 },
6530 },
6531 },
6532 },
6533 {
6534 input: []byte(`
6535groups:
6536- name: xxx
6537 labels:
6538 - foo: bar
6539 rules: []
6540`),
6541 output: parser.File{
6542 IsRelaxed: true,
6543 Groups: []parser.Group{
6544 {
6545 Name: parser.YamlNode{
6546 Value: "xxx",
6547 Pos: diags.PositionRanges{
6548 {Line: 3, FirstColumn: 9, LastColumn: 11},
6549 },
6550 },
6551 Labels: &parser.YamlMap{
6552 Key: &parser.YamlNode{
6553 Value: "labels",
6554 Pos: diags.PositionRanges{
6555 {Line: 4, FirstColumn: 3, LastColumn: 8},
6556 },
6557 },
6558 },
6559 },
6560 },
6561 },
6562 },
6563 {
6564 input: []byte(`
6565groups:
6566- name: xxx
6567 labels:
6568 foo: 1
6569 rules: []
6570`),
6571 output: parser.File{
6572 IsRelaxed: true,
6573 Groups: []parser.Group{
6574 {
6575 Name: parser.YamlNode{
6576 Value: "xxx",
6577 Pos: diags.PositionRanges{
6578 {Line: 3, FirstColumn: 9, LastColumn: 11},
6579 },
6580 },
6581 Labels: &parser.YamlMap{
6582 Key: &parser.YamlNode{
6583 Value: "labels",
6584 Pos: diags.PositionRanges{
6585 {Line: 4, FirstColumn: 3, LastColumn: 8},
6586 },
6587 },
6588 Items: []*parser.YamlKeyValue{
6589 {
6590 Key: &parser.YamlNode{
6591 Value: "foo",
6592 Pos: diags.PositionRanges{
6593 {Line: 5, FirstColumn: 5, LastColumn: 7},
6594 },
6595 },
6596 Value: &parser.YamlNode{
6597 Value: "1",
6598 Pos: diags.PositionRanges{
6599 {Line: 5, FirstColumn: 10, LastColumn: 10},
6600 },
6601 },
6602 },
6603 },
6604 },
6605 },
6606 },
6607 },
6608 },
6609 {
6610 input: []byte(`
6611groups:
6612- name: xxx
6613 labels:
6614 foo: bar
6615 bob: foo
6616 foo: bob
6617 rules: []
6618`),
6619 output: parser.File{
6620 IsRelaxed: true,
6621 Groups: []parser.Group{
6622 {
6623 Name: parser.YamlNode{
6624 Value: "xxx",
6625 Pos: diags.PositionRanges{
6626 {Line: 3, FirstColumn: 9, LastColumn: 11},
6627 },
6628 },
6629 Labels: &parser.YamlMap{
6630 Key: &parser.YamlNode{
6631 Value: "labels",
6632 Pos: diags.PositionRanges{
6633 {Line: 4, FirstColumn: 3, LastColumn: 8},
6634 },
6635 },
6636 Items: []*parser.YamlKeyValue{
6637 {
6638 Key: &parser.YamlNode{
6639 Value: "foo",
6640 Pos: diags.PositionRanges{
6641 {Line: 5, FirstColumn: 5, LastColumn: 7},
6642 },
6643 },
6644 Value: &parser.YamlNode{
6645 Value: "bar",
6646 Pos: diags.PositionRanges{
6647 {Line: 5, FirstColumn: 10, LastColumn: 12},
6648 },
6649 },
6650 },
6651 {
6652 Key: &parser.YamlNode{
6653 Value: "bob",
6654 Pos: diags.PositionRanges{
6655 {Line: 6, FirstColumn: 5, LastColumn: 7},
6656 },
6657 },
6658 Value: &parser.YamlNode{
6659 Value: "foo",
6660 Pos: diags.PositionRanges{
6661 {Line: 6, FirstColumn: 10, LastColumn: 12},
6662 },
6663 },
6664 },
6665 {
6666 Key: &parser.YamlNode{
6667 Value: "foo",
6668 Pos: diags.PositionRanges{
6669 {Line: 7, FirstColumn: 5, LastColumn: 7},
6670 },
6671 },
6672 Value: &parser.YamlNode{
6673 Value: "bob",
6674 Pos: diags.PositionRanges{
6675 {Line: 7, FirstColumn: 10, LastColumn: 12},
6676 },
6677 },
6678 },
6679 },
6680 },
6681 },
6682 },
6683 },
6684 },
6685 {
6686 input: []byte(`
6687- name: xxx
6688 interval: 3m
6689 query_offset: 1s
6690 limit: 5
6691 labels:
6692 foo: bar
6693 rules:
6694 - record: up:count
6695 expr: count(up)
6696`),
6697 output: parser.File{
6698 IsRelaxed: true,
6699 Groups: []parser.Group{
6700 {
6701 Name: parser.YamlNode{
6702 Value: "xxx",
6703 Pos: diags.PositionRanges{
6704 {Line: 2, FirstColumn: 9, LastColumn: 11},
6705 },
6706 },
6707 Interval: &parser.YamlDuration{
6708 Value: time.Minute * 3,
6709 Raw: "3m",
6710 Pos: diags.PositionRanges{
6711 {Line: 3, FirstColumn: 13, LastColumn: 14},
6712 },
6713 },
6714 QueryOffset: &parser.YamlDuration{
6715 Value: time.Second,
6716 Raw: "1s",
6717 Pos: diags.PositionRanges{
6718 {Line: 4, FirstColumn: 17, LastColumn: 18},
6719 },
6720 },
6721 Limit: &parser.YamlInt{
6722 Value: 5,
6723 Raw: "5",
6724 Pos: diags.PositionRanges{
6725 {Line: 5, FirstColumn: 10, LastColumn: 10},
6726 },
6727 },
6728 Labels: &parser.YamlMap{
6729 Key: &parser.YamlNode{
6730 Value: "labels",
6731 Pos: diags.PositionRanges{
6732 {Line: 6, FirstColumn: 3, LastColumn: 8},
6733 },
6734 },
6735 Items: []*parser.YamlKeyValue{
6736 {
6737 Key: &parser.YamlNode{
6738 Value: "foo",
6739 Pos: diags.PositionRanges{
6740 {Line: 7, FirstColumn: 5, LastColumn: 7},
6741 },
6742 },
6743 Value: &parser.YamlNode{
6744 Value: "bar",
6745 Pos: diags.PositionRanges{
6746 {Line: 7, FirstColumn: 10, LastColumn: 12},
6747 },
6748 },
6749 },
6750 },
6751 },
6752 Rules: []parser.Rule{
6753 {
6754 Lines: diags.LineRange{First: 9, Last: 10},
6755 RecordingRule: &parser.RecordingRule{
6756 Record: parser.YamlNode{
6757 Value: "up:count",
6758 Pos: diags.PositionRanges{
6759 {Line: 9, FirstColumn: 13, LastColumn: 20},
6760 },
6761 },
6762 Expr: parser.PromQLExpr{
6763 Value: &parser.YamlNode{
6764 Value: "count(up)",
6765 Pos: diags.PositionRanges{
6766 {Line: 10, FirstColumn: 11, LastColumn: 19},
6767 },
6768 },
6769 },
6770 },
6771 },
6772 },
6773 },
6774 },
6775 },
6776 },
6777 {
6778 input: []byte(`
6779
6780- interval: 3m
6781 query_offset: 1s
6782 limit: 5
6783 labels:
6784 foo: bar
6785 rules:
6786 - record: up:count
6787 expr: count(up)
6788`),
6789 output: parser.File{
6790 IsRelaxed: true,
6791 Groups: []parser.Group{
6792 {
6793 Name: parser.YamlNode{Value: ""},
6794 Rules: []parser.Rule{
6795 {
6796 Lines: diags.LineRange{First: 9, Last: 10},
6797 RecordingRule: &parser.RecordingRule{
6798 Record: parser.YamlNode{
6799 Value: "up:count",
6800 Pos: diags.PositionRanges{
6801 {Line: 9, FirstColumn: 13, LastColumn: 20},
6802 },
6803 },
6804 Expr: parser.PromQLExpr{
6805 Value: &parser.YamlNode{
6806 Value: "count(up)",
6807 Pos: diags.PositionRanges{
6808 {Line: 10, FirstColumn: 11, LastColumn: 19},
6809 },
6810 },
6811 },
6812 },
6813 },
6814 },
6815 },
6816 },
6817 },
6818 },
6819 {
6820 input: []byte(`
6821apiVersion: v1
6822kind: ConfigMap
6823metadata:
6824 labels:
6825 shard: "0"
6826 total-shards: "1"
6827 name: shard-jobs
6828 namespace: foo
6829data:
6830 jobs.yaml: |
6831 jobs:
6832 - name: foo
6833 interval: 1m
6834 queries:
6835 - name: xxx
6836 help: ''
6837 labels:
6838 - account_id
6839 - bucket
6840 - cache_status
6841 mtls_identity:
6842 cert_path: /etc/identity/tls.crt
6843 key_path: /etc/identity/tls.key
6844`),
6845 output: parser.File{
6846 Groups: nil,
6847 IsRelaxed: true,
6848 },
6849 },
6850 {
6851 // Test rule with line comment on the mapping node itself
6852 input: []byte(`- record: foo # pint disable line comment
6853 expr: bar
6854`),
6855 output: parser.File{
6856 IsRelaxed: true,
6857 Groups: []parser.Group{
6858 {
6859 Rules: []parser.Rule{
6860 {
6861 Lines: diags.LineRange{First: 1, Last: 2},
6862 Comments: []comments.Comment{
6863 {
6864 Type: comments.DisableType,
6865 Value: comments.Disable{
6866 Match: "line comment",
6867 Position: comments.Position{
6868 Offset: 15,
6869 Pos: diags.PositionRanges{
6870 {Line: 1, FirstColumn: 1, LastColumn: 27},
6871 },
6872 },
6873 },
6874 },
6875 },
6876 RecordingRule: &parser.RecordingRule{
6877 Record: parser.YamlNode{
6878 Value: "foo",
6879 Pos: diags.PositionRanges{
6880 {Line: 1, FirstColumn: 11, LastColumn: 13},
6881 },
6882 },
6883 Expr: parser.PromQLExpr{
6884 Value: &parser.YamlNode{
6885 Value: "bar",
6886 Pos: diags.PositionRanges{
6887 {Line: 2, FirstColumn: 9, LastColumn: 11},
6888 },
6889 },
6890 },
6891 },
6892 },
6893 },
6894 },
6895 },
6896 },
6897 },
6898 {
6899 // Test flow mapping with line comment (comment attached to parent node)
6900 input: []byte(`- {record: foo, expr: bar} # pint disable flow comment
6901`),
6902 output: parser.File{
6903 IsRelaxed: true,
6904 Groups: []parser.Group{
6905 {
6906 Rules: []parser.Rule{
6907 {
6908 Lines: diags.LineRange{First: 1, Last: 1},
6909 Comments: []comments.Comment{
6910 {
6911 Type: comments.DisableType,
6912 Value: comments.Disable{
6913 Match: "flow comment",
6914 Position: comments.Position{
6915 Offset: 15,
6916 Pos: diags.PositionRanges{
6917 {Line: 1, FirstColumn: 1, LastColumn: 27},
6918 },
6919 },
6920 },
6921 },
6922 },
6923 RecordingRule: &parser.RecordingRule{
6924 Record: parser.YamlNode{
6925 Value: "foo",
6926 Pos: diags.PositionRanges{
6927 {Line: 1, FirstColumn: 12, LastColumn: 14},
6928 },
6929 },
6930 Expr: parser.PromQLExpr{
6931 Value: &parser.YamlNode{
6932 Value: "bar",
6933 Pos: diags.PositionRanges{
6934 {Line: 1, FirstColumn: 23, LastColumn: 25},
6935 },
6936 },
6937 },
6938 },
6939 },
6940 },
6941 },
6942 },
6943 },
6944 },
6945 {
6946 // Test flow mapping with foot comment (comment attached to parent node's FootComment)
6947 input: []byte(`groups:
6948- name: test
6949 rules:
6950 - {record: foo, expr: bar}
6951 # pint disable foot comment
6952`),
6953 output: parser.File{
6954 IsRelaxed: true,
6955 Groups: []parser.Group{
6956 {
6957 Name: parser.YamlNode{
6958 Value: "test",
6959 Pos: diags.PositionRanges{
6960 {Line: 2, FirstColumn: 9, LastColumn: 12},
6961 },
6962 },
6963 Rules: []parser.Rule{
6964 {
6965 Lines: diags.LineRange{First: 4, Last: 4},
6966 Comments: []comments.Comment{
6967 {
6968 Type: comments.DisableType,
6969 Value: comments.Disable{
6970 Match: "foot comment",
6971 Position: comments.Position{
6972 Offset: 15,
6973 Pos: diags.PositionRanges{
6974 {Line: 5, FirstColumn: 3, LastColumn: 29},
6975 },
6976 },
6977 },
6978 },
6979 },
6980 RecordingRule: &parser.RecordingRule{
6981 Record: parser.YamlNode{
6982 Value: "foo",
6983 Pos: diags.PositionRanges{
6984 {Line: 4, FirstColumn: 14, LastColumn: 16},
6985 },
6986 },
6987 Expr: parser.PromQLExpr{
6988 Value: &parser.YamlNode{
6989 Value: "bar",
6990 Pos: diags.PositionRanges{
6991 {Line: 4, FirstColumn: 25, LastColumn: 27},
6992 },
6993 },
6994 },
6995 },
6996 },
6997 },
6998 },
6999 },
7000 },
7001 },
7002 {
7003 input: []byte(`
7004groups:
7005- name: v2
7006 rules:
7007 - record: comment:inside:expr
7008 expr: |
7009 count(
7010 foo
7011 # pint disable promql/series
7012 + bar
7013 )
7014`),
7015 strict: true,
7016 output: parser.File{
7017 Groups: []parser.Group{
7018 {
7019 Name: parser.YamlNode{
7020 Value: "v2",
7021 Pos: diags.PositionRanges{
7022 {Line: 3, FirstColumn: 9, LastColumn: 10},
7023 },
7024 },
7025 Rules: []parser.Rule{
7026 // FIXME should we parse comments inside PromQL?
7027 {
7028 Lines: diags.LineRange{First: 5, Last: 11},
7029 RecordingRule: &parser.RecordingRule{
7030 Record: parser.YamlNode{
7031 Value: "comment:inside:expr",
7032 Pos: diags.PositionRanges{
7033 {Line: 5, FirstColumn: 13, LastColumn: 31},
7034 },
7035 },
7036 Expr: parser.PromQLExpr{
7037 Value: &parser.YamlNode{
7038 Value: "count(\n foo\n # pint disable promql/series\n + bar\n)\n",
7039 Pos: diags.PositionRanges{
7040 {Line: 7, FirstColumn: 7, LastColumn: 13},
7041 {Line: 8, FirstColumn: 7, LastColumn: 12},
7042 {Line: 9, FirstColumn: 7, LastColumn: 37},
7043 {Line: 10, FirstColumn: 7, LastColumn: 14},
7044 {Line: 11, FirstColumn: 7, LastColumn: 7},
7045 },
7046 },
7047 },
7048 },
7049 },
7050 },
7051 },
7052 },
7053 },
7054 },
7055 {
7056 input: []byte(`
7057groups:
7058- name: v2
7059 rules:
7060 - record: comment:inside:expr
7061 expr: |
7062 count(
7063 foo
7064 # pint disable promql/series
7065 + bar
7066 )
7067`),
7068 output: parser.File{
7069 IsRelaxed: true,
7070 Groups: []parser.Group{
7071 {
7072 Name: parser.YamlNode{
7073 Value: "v2",
7074 Pos: diags.PositionRanges{
7075 {Line: 3, FirstColumn: 9, LastColumn: 10},
7076 },
7077 },
7078 Rules: []parser.Rule{
7079 // FIXME should we parse comments inside PromQL?
7080 {
7081 Lines: diags.LineRange{First: 5, Last: 11},
7082 RecordingRule: &parser.RecordingRule{
7083 Record: parser.YamlNode{
7084 Value: "comment:inside:expr",
7085 Pos: diags.PositionRanges{
7086 {Line: 5, FirstColumn: 13, LastColumn: 31},
7087 },
7088 },
7089 Expr: parser.PromQLExpr{
7090 Value: &parser.YamlNode{
7091 Value: "count(\n foo\n # pint disable promql/series\n + bar\n)\n",
7092 Pos: diags.PositionRanges{
7093 {Line: 7, FirstColumn: 7, LastColumn: 13},
7094 {Line: 8, FirstColumn: 7, LastColumn: 12},
7095 {Line: 9, FirstColumn: 7, LastColumn: 37},
7096 {Line: 10, FirstColumn: 7, LastColumn: 14},
7097 {Line: 11, FirstColumn: 7, LastColumn: 7},
7098 },
7099 },
7100 },
7101 },
7102 },
7103 },
7104 },
7105 },
7106 },
7107 },
7108 {
7109 input: []byte(`---
7110apiVersion: apps/v1
7111kind: StatefulSet
7112metadata:
7113 namespace: thanos
7114spec:
7115 replicas: 2
7116 selector:
7117 matchLabels:
7118 app: ruler-core
7119 component: ruler
7120 serviceName: thanos-ruler
7121 template:
7122 metadata:
7123 labels:
7124 app: ruler-core
7125 component: ruler
7126 spec:
7127 containers:
7128 - args:
7129 - --log.format=json
7130 - --http-address=0.0.0.0:10902
7131 - --grpc-address=0.0.0.0:10901
7132 - --eval-interval=1m
7133 - |
7134 --tracing.config=
7135 type: JAEGER
7136 config:
7137 sampler_param: 0.01
7138 sampler_type: probabilistic
7139 command:
7140 - /bin/thanos
7141 - rule
7142 imagePullPolicy: IfNotPresent
7143 livenessProbe:
7144 httpGet:
7145 path: /-/healthy
7146 port: 10902
7147 initialDelaySeconds: 30
7148 terminationGracePeriodSeconds: 300
7149 timeoutSeconds: 5
7150 name: ruler
7151 ports:
7152 - containerPort: 10902
7153 name: http-metrics
7154 readinessProbe:
7155 httpGet:
7156 path: /-/ready
7157 port: 10902
7158 initialDelaySeconds: 30
7159 timeoutSeconds: 1
7160 resources:
7161 limits:
7162 cpu: "4"
7163 memory: 4G
7164 requests:
7165 cpu: "4"
7166 memory: 4G
7167 - args:
7168 - --config=/rules/current/.pint.hcl
7169 - watch
7170 - --listen=:10904
7171 - --max-problems=50
7172 - glob
7173 - /rules/current/rules/*
7174 command:
7175 - /usr/local/bin/pint
7176 imagePullPolicy: IfNotPresent
7177 livenessProbe:
7178 httpGet:
7179 path: /health
7180 port: 10904
7181 initialDelaySeconds: 30
7182 timeoutSeconds: 5
7183 name: pint
7184 ports:
7185 - containerPort: 10904
7186 name: pint
7187 resources:
7188 limits:
7189 cpu: "1"
7190 memory: 256Mi
7191 requests:
7192 cpu: "1"
7193 memory: 256Mi
7194 volumeMounts:
7195 - mountPath: /rules
7196 name: rules
7197---
7198apiVersion: v1
7199kind: ConfigMap
7200metadata:
7201 labels:
7202 prometheus.cfplat.com/rules: "true"
7203 name: ruler-core-ruler
7204 namespace: thanos
7205data:
7206 rules: |
7207 groups:
7208 - name: ruler-health-alerts
7209 rules:
7210 - alert: Thanos_Rule_Queue_Is_Dropping_Alerts
7211 expr: rate(thanos_alert_queue_alerts_dropped_total{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]) > 0
7212 for: 5m
7213 labels:
7214 priority: "3"
7215 notify: chat-obs-metrics
7216 annotations:
7217 summary: Thanos Ruler is failing to queue alerts.
7218 - alert: Thanos_Rule_Sender_Is_Dropping_Alerts
7219 expr: rate(thanos_alert_sender_alerts_dropped_total{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]) > 0
7220 for: 5m
7221 labels:
7222 priority: "3"
7223 notify: chat-obs-metrics
7224 annotations:
7225 summary: Thanos Ruler is failing to send alerts.
7226 - alert: Thanos_Rule_High_Rule_Evaluation_Failures
7227 expr: |2-
7228
7229 (
7230 sum(rate(prometheus_rule_evaluation_failures_total{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]) / rate(prometheus_rule_group_last_duration_seconds{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]))
7231 by (job,kubernetes_name,pod, pod_app, pod_component)
7232 /
7233 sum(rate(prometheus_rule_evaluations_total{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]) / rate(prometheus_rule_group_last_duration_seconds{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]))
7234 by (job,kubernetes_name,pod, pod_app, pod_component)
7235 * 100 > 5
7236 )
7237 for: 5m
7238 labels:
7239 priority: "3"
7240 notify: chat-obs-metrics
7241 annotations:
7242 summary: Thanos Ruler is failing to evaluate {{ $value | humanize }}% of rules.
7243`),
7244 output: parser.File{
7245 IsRelaxed: true,
7246 Groups: []parser.Group{
7247 {
7248 Name: parser.YamlNode{
7249 Value: "ruler-health-alerts",
7250 Pos: diags.PositionRanges{
7251 {Line: 2, FirstColumn: 11, LastColumn: 29},
7252 },
7253 },
7254 Rules: []parser.Rule{
7255 {
7256 Lines: diags.LineRange{First: 102, Last: 109},
7257 AlertingRule: &parser.AlertingRule{
7258 Alert: parser.YamlNode{
7259 Value: "Thanos_Rule_Queue_Is_Dropping_Alerts",
7260 Pos: diags.PositionRanges{
7261 {Line: 102, FirstColumn: 20, LastColumn: 55},
7262 },
7263 },
7264 Expr: parser.PromQLExpr{
7265 Value: &parser.YamlNode{
7266 Value: `rate(thanos_alert_queue_alerts_dropped_total{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]) > 0`,
7267 Pos: diags.PositionRanges{
7268 {Line: 103, FirstColumn: 19, LastColumn: 147},
7269 },
7270 },
7271 },
7272 For: &parser.YamlDuration{
7273 Value: 5 * time.Minute,
7274 Raw: "5m",
7275 Pos: diags.PositionRanges{
7276 {Line: 104, FirstColumn: 18, LastColumn: 19},
7277 },
7278 },
7279 Labels: &parser.YamlMap{
7280 Key: &parser.YamlNode{
7281 Value: "labels",
7282 Pos: diags.PositionRanges{
7283 {Line: 105, FirstColumn: 13, LastColumn: 18},
7284 },
7285 },
7286 Items: []*parser.YamlKeyValue{
7287 {
7288 Key: &parser.YamlNode{
7289 Value: "priority",
7290 Pos: diags.PositionRanges{
7291 {Line: 106, FirstColumn: 15, LastColumn: 22},
7292 },
7293 },
7294 Value: &parser.YamlNode{
7295 Value: "3",
7296 Pos: diags.PositionRanges{
7297 {Line: 106, FirstColumn: 26, LastColumn: 26},
7298 },
7299 },
7300 },
7301 {
7302 Key: &parser.YamlNode{
7303 Value: "notify",
7304 Pos: diags.PositionRanges{
7305 {Line: 107, FirstColumn: 15, LastColumn: 20},
7306 },
7307 },
7308 Value: &parser.YamlNode{
7309 Value: "chat-obs-metrics",
7310 Pos: diags.PositionRanges{
7311 {Line: 107, FirstColumn: 23, LastColumn: 38},
7312 },
7313 },
7314 },
7315 },
7316 },
7317 Annotations: &parser.YamlMap{
7318 Key: &parser.YamlNode{
7319 Value: "annotations",
7320 Pos: diags.PositionRanges{
7321 {Line: 108, FirstColumn: 13, LastColumn: 23},
7322 },
7323 },
7324 Items: []*parser.YamlKeyValue{
7325 {
7326 Key: &parser.YamlNode{
7327 Value: "summary",
7328 Pos: diags.PositionRanges{
7329 {Line: 109, FirstColumn: 15, LastColumn: 21},
7330 },
7331 },
7332 Value: &parser.YamlNode{
7333 Value: "Thanos Ruler is failing to queue alerts.",
7334 Pos: diags.PositionRanges{
7335 {Line: 109, FirstColumn: 24, LastColumn: 63},
7336 },
7337 },
7338 },
7339 },
7340 },
7341 },
7342 },
7343 {
7344 Lines: diags.LineRange{First: 110, Last: 117},
7345 AlertingRule: &parser.AlertingRule{
7346 Alert: parser.YamlNode{
7347 Value: "Thanos_Rule_Sender_Is_Dropping_Alerts",
7348 Pos: diags.PositionRanges{
7349 {Line: 110, FirstColumn: 20, LastColumn: 56},
7350 },
7351 },
7352 Expr: parser.PromQLExpr{
7353 Value: &parser.YamlNode{
7354 Value: `rate(thanos_alert_sender_alerts_dropped_total{kubernetes_namespace="thanos", pod_app="ruler-core", pod_component="ruler"}[2m]) > 0`,
7355 Pos: diags.PositionRanges{
7356 {Line: 111, FirstColumn: 19, LastColumn: 148},
7357 },
7358 },
7359 },
7360 For: &parser.YamlDuration{
7361 Value: 5 * time.Minute,
7362 Raw: "5m",
7363 Pos: diags.PositionRanges{
7364 {Line: 112, FirstColumn: 18, LastColumn: 19},
7365 },
7366 },
7367 Labels: &parser.YamlMap{
7368 Key: &parser.YamlNode{
7369 Value: "labels",
7370 Pos: diags.PositionRanges{
7371 {Line: 113, FirstColumn: 13, LastColumn: 18},
7372 },
7373 },
7374 Items: []*parser.YamlKeyValue{
7375 {
7376 Key: &parser.YamlNode{
7377 Value: "priority",
7378 Pos: diags.PositionRanges{
7379 {Line: 114, FirstColumn: 15, LastColumn: 22},
7380 },
7381 },
7382 Value: &parser.YamlNode{
7383 Value: "3",
7384 Pos: diags.PositionRanges{
7385 {Line: 114, FirstColumn: 26, LastColumn: 26},
7386 },
7387 },
7388 },
7389 {
7390 Key: &parser.YamlNode{
7391 Value: "notify",
7392 Pos: diags.PositionRanges{
7393 {Line: 115, FirstColumn: 15, LastColumn: 20},
7394 },
7395 },
7396 Value: &parser.YamlNode{
7397 Value: "chat-obs-metrics",
7398 Pos: diags.PositionRanges{
7399 {Line: 115, FirstColumn: 23, LastColumn: 38},
7400 },
7401 },
7402 },
7403 },
7404 },
7405 Annotations: &parser.YamlMap{
7406 Key: &parser.YamlNode{
7407 Value: "annotations",
7408 Pos: diags.PositionRanges{
7409 {Line: 116, FirstColumn: 13, LastColumn: 23},
7410 },
7411 },
7412 Items: []*parser.YamlKeyValue{
7413 {
7414 Key: &parser.YamlNode{
7415 Value: "summary",
7416 Pos: diags.PositionRanges{
7417 {Line: 117, FirstColumn: 15, LastColumn: 21},
7418 },
7419 },
7420 Value: &parser.YamlNode{
7421 Value: "Thanos Ruler is failing to send alerts.",
7422 Pos: diags.PositionRanges{
7423 {Line: 117, FirstColumn: 24, LastColumn: 62},
7424 },
7425 },
7426 },
7427 },
7428 },
7429 },
7430 },
7431 {
7432 Lines: diags.LineRange{First: 118, Last: 134},
7433 AlertingRule: &parser.AlertingRule{
7434 Alert: parser.YamlNode{
7435 Value: "Thanos_Rule_High_Rule_Evaluation_Failures",
7436 Pos: diags.PositionRanges{
7437 {Line: 118, FirstColumn: 20, LastColumn: 60},
7438 },
7439 },
7440 Expr: parser.PromQLExpr{
7441 Value: &parser.YamlNode{
7442 Value: "\n (\n sum(rate(prometheus_rule_evaluation_failures_total{kubernetes_namespace=\"thanos\", pod_app=\"ruler-core\", pod_component=\"ruler\"}[2m]) / rate(prometheus_rule_group_last_duration_seconds{kubernetes_namespace=\"thanos\", pod_app=\"ruler-core\", pod_component=\"ruler\"}[2m]))\n by (job,kubernetes_name,pod, pod_app, pod_component)\n /\n sum(rate(prometheus_rule_evaluations_total{kubernetes_namespace=\"thanos\", pod_app=\"ruler-core\", pod_component=\"ruler\"}[2m]) / rate(prometheus_rule_group_last_duration_seconds{kubernetes_namespace=\"thanos\", pod_app=\"ruler-core\", pod_component=\"ruler\"}[2m]))\n by (job,kubernetes_name,pod, pod_app, pod_component)\n * 100 > 5\n )",
7443 Pos: diags.PositionRanges{
7444 {Line: 119, FirstColumn: 22, LastColumn: 22},
7445 {Line: 120, FirstColumn: 14, LastColumn: 15},
7446 {Line: 121, FirstColumn: 17, LastColumn: 36},
7447 {Line: 122, FirstColumn: 15, LastColumn: 301},
7448 {Line: 123, FirstColumn: 15, LastColumn: 89},
7449 {Line: 124, FirstColumn: 15, LastColumn: 36},
7450 {Line: 125, FirstColumn: 15, LastColumn: 293},
7451 {Line: 126, FirstColumn: 15, LastColumn: 89},
7452 {Line: 127, FirstColumn: 15, LastColumn: 44},
7453 {Line: 128, FirstColumn: 15, LastColumn: 35},
7454 },
7455 },
7456 },
7457 For: &parser.YamlDuration{
7458 Value: 5 * time.Minute,
7459 Raw: "5m",
7460 Pos: diags.PositionRanges{
7461 {Line: 129, FirstColumn: 18, LastColumn: 19},
7462 },
7463 },
7464 Labels: &parser.YamlMap{
7465 Key: &parser.YamlNode{
7466 Value: "labels",
7467 Pos: diags.PositionRanges{
7468 {Line: 130, FirstColumn: 13, LastColumn: 18},
7469 },
7470 },
7471 Items: []*parser.YamlKeyValue{
7472 {
7473 Key: &parser.YamlNode{
7474 Value: "priority",
7475 Pos: diags.PositionRanges{
7476 {Line: 131, FirstColumn: 15, LastColumn: 22},
7477 },
7478 },
7479 Value: &parser.YamlNode{
7480 Value: "3",
7481 Pos: diags.PositionRanges{
7482 {Line: 131, FirstColumn: 26, LastColumn: 26},
7483 },
7484 },
7485 },
7486 {
7487 Key: &parser.YamlNode{
7488 Value: "notify",
7489 Pos: diags.PositionRanges{
7490 {Line: 132, FirstColumn: 15, LastColumn: 20},
7491 },
7492 },
7493 Value: &parser.YamlNode{
7494 Value: "chat-obs-metrics",
7495 Pos: diags.PositionRanges{
7496 {Line: 132, FirstColumn: 23, LastColumn: 38},
7497 },
7498 },
7499 },
7500 },
7501 },
7502 Annotations: &parser.YamlMap{
7503 Key: &parser.YamlNode{
7504 Value: "annotations",
7505 Pos: diags.PositionRanges{
7506 {Line: 133, FirstColumn: 13, LastColumn: 23},
7507 },
7508 },
7509 Items: []*parser.YamlKeyValue{
7510 {
7511 Key: &parser.YamlNode{
7512 Value: "summary",
7513 Pos: diags.PositionRanges{
7514 {Line: 134, FirstColumn: 15, LastColumn: 21},
7515 },
7516 },
7517 Value: &parser.YamlNode{
7518 Value: "Thanos Ruler is failing to evaluate {{ $value | humanize }}% of rules.",
7519 Pos: diags.PositionRanges{
7520 {Line: 134, FirstColumn: 24, LastColumn: 93},
7521 },
7522 },
7523 },
7524 },
7525 },
7526 },
7527 },
7528 },
7529 },
7530 },
7531 },
7532 },
7533 }
7534
7535 alwaysEqual := cmp.Comparer(func(_, _ any) bool { return true })
7536 ignorePrometheusExpr := cmp.FilterValues(func(x, y any) bool {
7537 _, xe := x.(*parser.PromQLNode)
7538 _, ye := y.(*parser.PromQLNode)
7539 return xe || ye
7540 }, alwaysEqual)
7541
7542 cmpErrorText := cmp.Comparer(func(x, y any) bool {
7543 xe := x.(error)
7544 ye := y.(error)
7545 return xe.Error() == ye.Error()
7546 })
7547 sameErrorText := cmp.FilterValues(func(x, y any) bool {
7548 _, xe := x.(error)
7549 _, ye := y.(error)
7550 return xe && ye
7551 }, cmpErrorText)
7552
7553 for i, tc := range testCases {
7554 t.Run(strconv.Itoa(i+1), func(t *testing.T) {
7555 t.Logf("\n--- Content ---%s--- END ---", tc.input)
7556
7557 s := bufio.NewScanner(bytes.NewReader(tc.input))
7558 for s.Scan() {
7559 tc.output.TotalLines++
7560 }
7561
7562 p := parser.NewParser(parser.Options{IsStrict: tc.strict, Schema: tc.schema, Names: tc.names})
7563 file := p.Parse(bytes.NewReader(tc.input))
7564
7565 if diff := cmp.Diff(
7566 tc.output, file,
7567 ignorePrometheusExpr,
7568 sameErrorText,
7569 cmpopts.IgnoreUnexported(parser.PromQLExpr{}),
7570 ); diff != "" {
7571 t.Errorf("Parse() returned wrong output (-want +got):\n%s", diff)
7572 return
7573 }
7574 })
7575 }
7576}
7577
7578func TestPromQLExprSyntaxError(t *testing.T) {
7579 type testCaseT struct {
7580 name string
7581 expectedError string
7582 input []byte
7583 schema parser.Schema
7584 names model.ValidationScheme
7585 }
7586
7587 testCases := []testCaseT{
7588 {
7589 name: "invalid label matching operator",
7590 input: []byte(`
7591groups:
7592- name: foo
7593 rules:
7594 - record: foo
7595 expr: |
7596 {'up' == 1}
7597`),
7598 expectedError: `1:8: parse error: unexpected "=" in label matching, expected string`,
7599 schema: parser.PrometheusSchema,
7600 names: model.LegacyValidation,
7601 },
7602 {
7603 name: "unclosed parenthesis",
7604 input: []byte(`
7605groups:
7606- name: foo
7607 rules:
7608 - record: foo
7609 expr: sum(up
7610`),
7611 expectedError: `1:7: parse error: unclosed left parenthesis`,
7612 schema: parser.PrometheusSchema,
7613 names: model.LegacyValidation,
7614 },
7615 {
7616 name: "unclosed brace",
7617 input: []byte(`
7618groups:
7619- name: foo
7620 rules:
7621 - record: foo
7622 expr: up{job="test"
7623`),
7624 expectedError: `1:14: parse error: unexpected end of input inside braces`,
7625 schema: parser.PrometheusSchema,
7626 names: model.LegacyValidation,
7627 },
7628 {
7629 name: "invalid aggregation without grouping",
7630 input: []byte(`
7631groups:
7632- name: foo
7633 rules:
7634 - record: foo
7635 expr: sum without (up)
7636`),
7637 expectedError: `1:17: parse error: unexpected end of input in aggregation`,
7638 schema: parser.PrometheusSchema,
7639 names: model.LegacyValidation,
7640 },
7641 {
7642 name: "bad regex syntax",
7643 input: []byte(`
7644groups:
7645- name: foo
7646 rules:
7647 - record: foo
7648 expr: up{job=~"["}
7649`),
7650 expectedError: `1:4: parse error: error parsing regexp: missing closing ]: ` + "`[`",
7651 schema: parser.PrometheusSchema,
7652 names: model.LegacyValidation,
7653 },
7654 {
7655 name: "invalid binary operator position",
7656 input: []byte(`
7657groups:
7658- name: foo
7659 rules:
7660 - record: foo
7661 expr: up +
7662`),
7663 expectedError: `1:5: parse error: unexpected end of input`,
7664 schema: parser.PrometheusSchema,
7665 names: model.LegacyValidation,
7666 },
7667 {
7668 name: "multiple binary operators",
7669 input: []byte(`
7670groups:
7671- name: foo
7672 rules:
7673 - record: foo
7674 expr: up + * down
7675`),
7676 expectedError: `1:6: parse error: unexpected <op:*>`,
7677 schema: parser.PrometheusSchema,
7678 names: model.LegacyValidation,
7679 },
7680 {
7681 name: "invalid duration syntax",
7682 input: []byte(`
7683groups:
7684- name: foo
7685 rules:
7686 - record: foo
7687 expr: rate(up[5x])
7688`),
7689 expectedError: `1:9: parse error: bad number or duration syntax: "5"`,
7690 schema: parser.PrometheusSchema,
7691 names: model.LegacyValidation,
7692 },
7693 {
7694 name: "missing range selector",
7695 input: []byte(`
7696groups:
7697- name: foo
7698 rules:
7699 - record: foo
7700 expr: rate(up)
7701`),
7702 expectedError: `1:6: parse error: expected type range vector in call to function "rate", got instant vector`,
7703 schema: parser.PrometheusSchema,
7704 names: model.LegacyValidation,
7705 },
7706 {
7707 name: "invalid label name in matcher",
7708 input: []byte(`
7709groups:
7710- name: foo
7711 rules:
7712 - record: foo
7713 expr: up{123="test"}
7714`),
7715 expectedError: `1:4: parse error: unexpected character inside braces: '1'`,
7716 schema: parser.PrometheusSchema,
7717 names: model.LegacyValidation,
7718 },
7719 {
7720 name: "unclosed parenthesis with Thanos schema",
7721 input: []byte(`
7722groups:
7723- name: foo
7724 rules:
7725 - record: foo
7726 expr: sum(up
7727`),
7728 expectedError: `1:7: parse error: unclosed left parenthesis`,
7729 schema: parser.ThanosSchema,
7730 names: model.LegacyValidation,
7731 },
7732 {
7733 name: "invalid binary operator with UTF8 validation",
7734 input: []byte(`
7735groups:
7736- name: foo
7737 rules:
7738 - record: foo
7739 expr: up +
7740`),
7741 expectedError: `1:5: parse error: unexpected end of input`,
7742 schema: parser.PrometheusSchema,
7743 names: model.UTF8Validation,
7744 },
7745 {
7746 name: "bad regex with Thanos schema and UTF8 validation",
7747 input: []byte(`
7748groups:
7749- name: foo
7750 rules:
7751 - record: foo
7752 expr: up{job=~"["}
7753`),
7754 expectedError: `1:4: parse error: error parsing regexp: missing closing ]: ` + "`[`",
7755 schema: parser.ThanosSchema,
7756 names: model.UTF8Validation,
7757 },
7758 }
7759
7760 for _, tc := range testCases {
7761 t.Run(tc.name, func(t *testing.T) {
7762 p := parser.NewParser(parser.Options{Schema: tc.schema, Names: tc.names})
7763 r := bytes.NewReader(tc.input)
7764 file := p.Parse(r)
7765
7766 require.NoError(t, file.Error.Err)
7767 require.NotEmpty(t, file.Groups)
7768 for _, group := range file.Groups {
7769 for _, rule := range group.Rules {
7770 require.NoError(t, rule.Error.Err)
7771 expr := rule.Expr()
7772 err := expr.SyntaxError()
7773 require.Error(t, err)
7774 require.Equal(t, tc.expectedError, err.Error())
7775 }
7776 }
7777 })
7778 }
7779}
7780
7781func BenchmarkParse(b *testing.B) {
7782 data, err := os.ReadFile("testrules.yml")
7783 require.NoError(b, err)
7784
7785 p := parser.NewParser(parser.Options{IsStrict: true, Names: model.LegacyValidation})
7786 for b.Loop() {
7787 b.StopTimer()
7788 r := bytes.NewReader(data)
7789 b.StartTimer()
7790
7791 f := p.Parse(r)
7792
7793 b.StopTimer()
7794 require.Len(b, f.Groups, 90)
7795 require.NoError(b, f.Error.Err)
7796 require.Equal(b, 5501, f.TotalLines)
7797 b.StartTimer()
7798 }
7799}
7800