cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.24.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/parser_test.go

1248lines · modeblame

a68c8b60Lukasz Mierzwa5 years ago1package parser_test
2
3import (
4"fmt"
5"strconv"
6"testing"
7
8"github.com/cloudflare/pint/internal/parser"
9
10"github.com/google/go-cmp/cmp"
11promparser "github.com/prometheus/prometheus/promql/parser"
12)
13
14func TestParse(t *testing.T) {
15type testCaseT struct {
16content []byte
17output []parser.Rule
18shouldError bool
19}
20
21testCases := []testCaseT{
22{
23content: nil,
24output: nil,
25shouldError: false,
26},
27{
28content: []byte{},
29output: nil,
30shouldError: false,
31},
7d1ea938Lukasz Mierzwa4 years ago32{
33content: []byte(string("! !00 \xf6")),
34output: nil,
35shouldError: true,
36},
37{
014cdb29Lukasz Mierzwa4 years ago38content: []byte("- 0: 0\n 00000000: 000000\n 000000:00000000000: 00000000\n 00000000000:000000: 0000000000000000000000000000000000\n 000000: 0000000\n expr: |"),
7d1ea938Lukasz Mierzwa4 years ago39output: []parser.Rule{
40{Error: parser.ParseError{Err: fmt.Errorf("incomplete rule, no alert or record key"), Line: 6}},
41},
42},
a68c8b60Lukasz Mierzwa5 years ago43{
44content: []byte("- record: |\n multiline\n"),
45output: []parser.Rule{
46{Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 1}},
47},
48},
49{
50content: []byte("- expr: foo\n"),
51output: []parser.Rule{
52{Error: parser.ParseError{Err: fmt.Errorf("incomplete rule, no alert or record key"), Line: 1}},
53},
54},
55{
56content: []byte("- alert: foo\n"),
57output: []parser.Rule{
58{Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 1}},
59},
60},
61{
62content: []byte("- alert: foo\n record: foo\n"),
63output: []parser.Rule{
64{Error: parser.ParseError{Err: fmt.Errorf("got both record and alert keys in a single rule"), Line: 1}},
65},
66},
67{
68content: []byte("- record: foo\n labels:\n foo: bar\n"),
69output: []parser.Rule{
70{Error: parser.ParseError{Err: fmt.Errorf("missing expr key"), Line: 1}},
71},
72},
73{
74content: []byte("- record: - foo\n"),
75shouldError: true,
76},
77{
78content: []byte("- record: foo expr: sum(\n"),
79shouldError: true,
80},
81{
82content: []byte("- record\n\texpr: foo\n"),
83shouldError: true,
84},
14ab07ffLukasz Mierzwa4 years ago85{
86content: []byte(`
87- record: foo
88expr: bar
89expr: bar
90`),
91output: []parser.Rule{
58ae7de1Lukasz Mierzwa4 years ago92{Error: parser.ParseError{Err: fmt.Errorf("duplicated expr key"), Line: 4}},
14ab07ffLukasz Mierzwa4 years ago93},
94},
95{
96content: []byte(`
97- record: foo
98expr: bar
99record: bar
100`),
101output: []parser.Rule{
58ae7de1Lukasz Mierzwa4 years ago102{Error: parser.ParseError{Err: fmt.Errorf("duplicated record key"), Line: 4}},
14ab07ffLukasz Mierzwa4 years ago103},
104},
105{
106content: []byte(`
107- alert: foo
108alert: bar
109expr: bar
110`),
111output: []parser.Rule{
58ae7de1Lukasz Mierzwa4 years ago112{Error: parser.ParseError{Err: fmt.Errorf("duplicated alert key"), Line: 3}},
14ab07ffLukasz Mierzwa4 years ago113},
114},
115{
116content: []byte(`
117- alert: foo
118for: 5m
119expr: bar
120for: 1m
121`),
122output: []parser.Rule{
58ae7de1Lukasz Mierzwa4 years ago123{Error: parser.ParseError{Err: fmt.Errorf("duplicated for key"), Line: 5}},
14ab07ffLukasz Mierzwa4 years ago124},
125},
126{
127content: []byte(`
128- alert: foo
129labels: {}
130expr: bar
131labels: {}
132`),
133output: []parser.Rule{
58ae7de1Lukasz Mierzwa4 years ago134{Error: parser.ParseError{Err: fmt.Errorf("duplicated labels key"), Line: 5}},
14ab07ffLukasz Mierzwa4 years ago135},
136},
137{
138content: []byte(`
139- record: foo
140labels: {}
141expr: bar
142labels: {}
143`),
144output: []parser.Rule{
58ae7de1Lukasz Mierzwa4 years ago145{Error: parser.ParseError{Err: fmt.Errorf("duplicated labels key"), Line: 5}},
14ab07ffLukasz Mierzwa4 years ago146},
147},
148{
149content: []byte(`
150- alert: foo
151annotations: {}
152expr: bar
153annotations: {}
154`),
155output: []parser.Rule{
58ae7de1Lukasz Mierzwa4 years ago156{Error: parser.ParseError{Err: fmt.Errorf("duplicated annotations key"), Line: 5}},
14ab07ffLukasz Mierzwa4 years ago157},
158},
a68c8b60Lukasz Mierzwa5 years ago159{
160content: []byte("- record: foo\n expr: foo\n extra: true\n"),
161output: []parser.Rule{
162{Error: parser.ParseError{Err: fmt.Errorf("invalid key(s) found: extra"), Line: 3}},
163},
164},
165{
166content: []byte("- record: foo\n expr: foo offset 10m\n"),
167output: []parser.Rule{
168{
169RecordingRule: &parser.RecordingRule{
170Record: parser.YamlKeyValue{
171Key: &parser.YamlNode{
172Position: parser.FilePosition{Lines: []int{1}},
173Value: "record",
174},
175Value: &parser.YamlNode{
176Position: parser.FilePosition{Lines: []int{1}},
177Value: "foo",
178},
179},
180Expr: parser.PromQLExpr{
181Key: &parser.YamlNode{
182Position: parser.FilePosition{Lines: []int{2}},
183Value: "expr",
184},
185Value: &parser.YamlNode{
186Position: parser.FilePosition{Lines: []int{2}},
187Value: "foo offset 10m",
188},
189Query: &parser.PromQLNode{
190Expr: "foo offset 10m",
191},
192},
193},
194},
195},
196},
6c8e6966Lukasz Mierzwa4 years ago197{
198content: []byte("- record: foo\n expr: foo offset -10m\n"),
199output: []parser.Rule{
200{
201RecordingRule: &parser.RecordingRule{
202Record: parser.YamlKeyValue{
203Key: &parser.YamlNode{
204Position: parser.FilePosition{Lines: []int{1}},
205Value: "record",
206},
207Value: &parser.YamlNode{
208Position: parser.FilePosition{Lines: []int{1}},
209Value: "foo",
210},
211},
212Expr: parser.PromQLExpr{
213Key: &parser.YamlNode{
214Position: parser.FilePosition{Lines: []int{2}},
215Value: "expr",
216},
217Value: &parser.YamlNode{
218Position: parser.FilePosition{Lines: []int{2}},
219Value: "foo offset -10m",
220},
221Query: &parser.PromQLNode{
222Expr: "foo offset -10m",
223},
224},
225},
226},
227},
228},
032875e9Lukasz Mierzwa5 years ago229{
230content: []byte(`
231# head comment
232- record: foo # record comment
233expr: foo offset 10m # expr comment
234# pre-labels comment
235labels:
236# pre-foo comment
237foo: bar
238# post-foo comment
239bob: alice
240# foot comment
241`),
242output: []parser.Rule{
243{
244RecordingRule: &parser.RecordingRule{
245Record: parser.YamlKeyValue{
246Key: &parser.YamlNode{
247Position: parser.FilePosition{Lines: []int{3}},
248Value: "record",
249Comments: []string{"# head comment"},
250},
251Value: &parser.YamlNode{
252Position: parser.FilePosition{Lines: []int{3}},
253Value: "foo",
254Comments: []string{"# record comment"},
255},
256},
257Expr: parser.PromQLExpr{
258Key: &parser.YamlNode{
259Position: parser.FilePosition{Lines: []int{4}},
260Value: "expr",
261},
262Value: &parser.YamlNode{
263Position: parser.FilePosition{Lines: []int{4}},
264Value: "foo offset 10m",
265Comments: []string{"# expr comment"},
266},
267Query: &parser.PromQLNode{
268Expr: "foo offset 10m",
269},
270},
271Labels: &parser.YamlMap{
272Key: &parser.YamlNode{
273Position: parser.FilePosition{Lines: []int{6}},
274Value: "labels",
275Comments: []string{"# pre-labels comment"},
276},
277Items: []*parser.YamlKeyValue{
278{
279Key: &parser.YamlNode{
280Position: parser.FilePosition{Lines: []int{8}},
281Value: "foo",
282Comments: []string{"# pre-foo comment"},
283},
284Value: &parser.YamlNode{
285Position: parser.FilePosition{Lines: []int{8}},
286Value: "bar",
287},
288},
289{
290Key: &parser.YamlNode{
291Position: parser.FilePosition{Lines: []int{10}},
292Value: "bob",
293Comments: []string{"# post-foo comment"},
294},
295Value: &parser.YamlNode{
296Position: parser.FilePosition{Lines: []int{10}},
297Value: "alice",
298},
299},
300},
301},
302},
303},
304},
305},
a68c8b60Lukasz Mierzwa5 years ago306{
307content: []byte("- record: foo\n expr: foo[5m] offset 10m\n"),
308output: []parser.Rule{
309{
310RecordingRule: &parser.RecordingRule{
311Record: parser.YamlKeyValue{
312Key: &parser.YamlNode{
313Position: parser.FilePosition{Lines: []int{1}},
314Value: "record",
315},
316Value: &parser.YamlNode{
317Position: parser.FilePosition{Lines: []int{1}},
318Value: "foo",
319},
320},
321Expr: parser.PromQLExpr{
322Key: &parser.YamlNode{
323Position: parser.FilePosition{Lines: []int{2}},
324Value: "expr",
325},
326Value: &parser.YamlNode{
327Position: parser.FilePosition{Lines: []int{2}},
328Value: "foo[5m] offset 10m",
329},
330Query: &parser.PromQLNode{
331Expr: "foo[5m] offset 10m",
332Children: []*parser.PromQLNode{
333{Expr: "foo offset 10m"},
334},
335},
336},
337},
338},
339},
340},
341{
342content: []byte(`
343- record: name
344expr: sum(foo)
345labels:
346foo: bar
347bob: alice
348`),
349output: []parser.Rule{
350{
351RecordingRule: &parser.RecordingRule{
352Record: parser.YamlKeyValue{
353Key: &parser.YamlNode{
354Position: parser.FilePosition{Lines: []int{2}},
355Value: "record",
356},
357Value: &parser.YamlNode{
358Position: parser.FilePosition{Lines: []int{2}},
359Value: "name",
360},
361},
362Expr: parser.PromQLExpr{
363Key: &parser.YamlNode{
364Position: parser.FilePosition{Lines: []int{3}},
365Value: "expr",
366},
367Value: &parser.YamlNode{
368Position: parser.FilePosition{Lines: []int{3}},
369Value: "sum(foo)",
370},
371Query: &parser.PromQLNode{
372Expr: "sum(foo)",
373Children: []*parser.PromQLNode{
374{Expr: "foo"},
375},
376},
377},
378Labels: &parser.YamlMap{
379Key: &parser.YamlNode{
380Position: parser.FilePosition{Lines: []int{4}},
381Value: "labels",
382},
383Items: []*parser.YamlKeyValue{
384{
385Key: &parser.YamlNode{
386Position: parser.FilePosition{Lines: []int{5}},
387Value: "foo",
388},
389Value: &parser.YamlNode{
390Position: parser.FilePosition{Lines: []int{5}},
391Value: "bar",
392},
393},
394{
395Key: &parser.YamlNode{
396Position: parser.FilePosition{Lines: []int{6}},
397Value: "bob",
398},
399Value: &parser.YamlNode{
400Position: parser.FilePosition{Lines: []int{6}},
401Value: "alice",
402},
403},
404},
405},
406},
407},
408},
409shouldError: false,
410},
411{
412content: []byte(`
413groups:
414- name: custom_rules
415rules:
416- record: name
417expr: sum(foo)
418labels:
419foo: bar
420bob: alice
421`),
422output: []parser.Rule{
423{
424RecordingRule: &parser.RecordingRule{
425Record: parser.YamlKeyValue{
426Key: &parser.YamlNode{
427Position: parser.FilePosition{Lines: []int{5}},
428Value: "record",
429},
430Value: &parser.YamlNode{
431Position: parser.FilePosition{Lines: []int{5}},
432Value: "name",
433},
434},
435Expr: parser.PromQLExpr{
436Key: &parser.YamlNode{
437Position: parser.FilePosition{Lines: []int{6}},
438Value: "expr",
439},
440Value: &parser.YamlNode{
441Position: parser.FilePosition{Lines: []int{6}},
442Value: "sum(foo)",
443},
444Query: &parser.PromQLNode{
445Expr: "sum(foo)",
446Children: []*parser.PromQLNode{
447{Expr: "foo"},
448},
449},
450},
451Labels: &parser.YamlMap{
452Key: &parser.YamlNode{
453Position: parser.FilePosition{Lines: []int{7}},
454Value: "labels",
455},
456Items: []*parser.YamlKeyValue{
457{
458Key: &parser.YamlNode{
459Position: parser.FilePosition{Lines: []int{8}},
460Value: "foo",
461},
462Value: &parser.YamlNode{
463Position: parser.FilePosition{Lines: []int{8}},
464Value: "bar",
465},
466},
467{
468Key: &parser.YamlNode{
469Position: parser.FilePosition{Lines: []int{9}},
470Value: "bob",
471},
472Value: &parser.YamlNode{
473Position: parser.FilePosition{Lines: []int{9}},
474Value: "alice",
475},
476},
477},
478},
479},
480},
481},
482shouldError: false,
483},
484{
485content: []byte(`- alert: Down
486expr: |
487up == 0
488for: |+
48911m
490labels:
491severity: critical
492annotations:
493uri: https://docs.example.com/down.html
494
495- record: >
496foo
497expr: |-
498bar
499/
500baz > 1
501labels: {}
502`),
503output: []parser.Rule{
504{
505AlertingRule: &parser.AlertingRule{
506Alert: parser.YamlKeyValue{
507Key: &parser.YamlNode{
508Position: parser.FilePosition{Lines: []int{1}},
509Value: "alert",
510},
511Value: &parser.YamlNode{
512Position: parser.FilePosition{Lines: []int{1}},
513Value: "Down",
514},
515},
516Expr: parser.PromQLExpr{
517Key: &parser.YamlNode{
518Position: parser.FilePosition{Lines: []int{2}},
519Value: "expr",
520},
521Value: &parser.YamlNode{
522Position: parser.FilePosition{Lines: []int{3}},
523Value: "up == 0\n",
524},
525Query: &parser.PromQLNode{
526Expr: "up == 0\n",
527Children: []*parser.PromQLNode{
528{Expr: "up"},
529{Expr: "0"},
530},
531},
532},
533For: &parser.YamlKeyValue{
534Key: &parser.YamlNode{
535Position: parser.FilePosition{Lines: []int{4}},
536Value: "for",
537},
d00835a5Lukasz Mierzwa4 years ago538Value: &parser.YamlNode{
539Position: parser.FilePosition{Lines: []int{5}},
540Value: "11m\n",
a68c8b60Lukasz Mierzwa5 years ago541},
542},
543Labels: &parser.YamlMap{
544Key: &parser.YamlNode{
545Position: parser.FilePosition{Lines: []int{6}},
546Value: "labels",
547},
548Items: []*parser.YamlKeyValue{
549{
550Key: &parser.YamlNode{
551Position: parser.FilePosition{Lines: []int{7}},
552Value: "severity",
553},
554Value: &parser.YamlNode{
555Position: parser.FilePosition{Lines: []int{7}},
556Value: "critical",
557},
558},
559},
560},
561Annotations: &parser.YamlMap{
562Key: &parser.YamlNode{
563Position: parser.FilePosition{Lines: []int{8}},
564Value: "annotations",
565},
566Items: []*parser.YamlKeyValue{
567{
568Key: &parser.YamlNode{
569Position: parser.FilePosition{Lines: []int{9}},
570Value: "uri",
571},
572Value: &parser.YamlNode{
573Position: parser.FilePosition{Lines: []int{9}},
574Value: "https://docs.example.com/down.html",
575},
576},
577},
578},
579},
580},
581{
582RecordingRule: &parser.RecordingRule{
583Record: parser.YamlKeyValue{
584Key: &parser.YamlNode{
585Position: parser.FilePosition{Lines: []int{11}},
586Value: "record",
587},
588Value: &parser.YamlNode{
589Position: parser.FilePosition{Lines: []int{12}},
590Value: "foo\n",
591},
592},
593Expr: parser.PromQLExpr{
594Key: &parser.YamlNode{
595Position: parser.FilePosition{Lines: []int{13}},
596Value: "expr",
597},
598Value: &parser.YamlNode{
599Position: parser.FilePosition{Lines: []int{14, 15, 16}},
600Value: "bar\n/\nbaz > 1",
601},
602Query: &parser.PromQLNode{
603Expr: "bar\n/\nbaz > 1",
604Children: []*parser.PromQLNode{
d00835a5Lukasz Mierzwa4 years ago605{
606Expr: "bar / baz", Children: []*parser.PromQLNode{
607{Expr: "bar"},
608{Expr: "baz"},
609},
a68c8b60Lukasz Mierzwa5 years ago610},
611{Expr: "1"},
612},
613},
614},
615Labels: &parser.YamlMap{
616Key: &parser.YamlNode{
617Position: parser.FilePosition{Lines: []int{17}},
618Value: "labels",
619},
620},
621},
622},
623},
624shouldError: false,
625},
626{
627content: []byte(`- alert: Foo
628expr:
629(
630xxx
631-
632yyy
633) * bar > 0
634and on(instance, device) baz
635for: 30m
636`),
637output: []parser.Rule{
638{
639AlertingRule: &parser.AlertingRule{
640Alert: parser.YamlKeyValue{
641Key: &parser.YamlNode{
642Position: parser.FilePosition{Lines: []int{1}},
643Value: "alert",
644},
645Value: &parser.YamlNode{
646Position: parser.FilePosition{Lines: []int{1}},
647Value: "Foo",
648},
649},
650Expr: parser.PromQLExpr{
651Key: &parser.YamlNode{
652Position: parser.FilePosition{Lines: []int{2}},
653Value: "expr",
654},
655Value: &parser.YamlNode{
656Position: parser.FilePosition{Lines: []int{3, 4, 5, 6, 7, 8}},
657Value: "( xxx - yyy ) * bar > 0 and on(instance, device) baz",
658},
659Query: &parser.PromQLNode{
660Expr: "( xxx - yyy ) * bar > 0 and on(instance, device) baz",
661Children: []*parser.PromQLNode{
662{
663Expr: "(xxx - yyy) * bar > 0",
664Children: []*parser.PromQLNode{
665{
666Expr: "(xxx - yyy) * bar",
667Children: []*parser.PromQLNode{
668{
669Expr: "(xxx - yyy)",
670Children: []*parser.PromQLNode{
671{
672Expr: "xxx - yyy",
673Children: []*parser.PromQLNode{
674{Expr: "xxx"},
675{Expr: "yyy"},
676},
677},
678},
679},
680{
681Expr: "bar",
682},
683},
684},
685{
686Expr: "0",
687},
688},
689},
690{Expr: "baz"},
691},
692},
693},
694For: &parser.YamlKeyValue{
695Key: &parser.YamlNode{
696Position: parser.FilePosition{Lines: []int{9}},
697Value: "for",
698},
d00835a5Lukasz Mierzwa4 years ago699Value: &parser.YamlNode{
700Position: parser.FilePosition{Lines: []int{9}},
701Value: "30m",
a68c8b60Lukasz Mierzwa5 years ago702},
703},
704},
705},
706},
707},
708{
709content: []byte(`---
710kind: ConfigMap
711apiVersion: v1
712metadata:
713name: example-app-alerts
714labels:
715app: example-app
716data:
717alerts: |
718groups:
719- name: example-app-alerts
720rules:
721- alert: Example_Is_Down
722expr: kube_deployment_status_replicas_available{namespace="example-app"} < 1
723for: 5m
724labels:
725priority: "2"
726environment: production
727annotations:
728summary: "No replicas for Example have been running for 5 minutes"
729
730- alert: Example_High_Restart_Rate
731expr: sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )
732`),
07e84896Lukasz Mierzwa4 years ago733output: []parser.Rule{
734{
735AlertingRule: &parser.AlertingRule{
736Alert: parser.YamlKeyValue{
737Key: &parser.YamlNode{
738Position: parser.FilePosition{Lines: []int{13}},
739Value: "alert",
740},
741Value: &parser.YamlNode{
742Position: parser.FilePosition{Lines: []int{13}},
743Value: "Example_Is_Down",
744},
745},
746Expr: parser.PromQLExpr{
747Key: &parser.YamlNode{
748Position: parser.FilePosition{Lines: []int{14}},
749Value: "expr",
750},
751Value: &parser.YamlNode{
752Position: parser.FilePosition{Lines: []int{14}},
753Value: `kube_deployment_status_replicas_available{namespace="example-app"} < 1`,
754},
755Query: &parser.PromQLNode{
756Expr: `kube_deployment_status_replicas_available{namespace="example-app"} < 1`,
757Children: []*parser.PromQLNode{
758{Expr: `kube_deployment_status_replicas_available{namespace="example-app"}`},
759{Expr: "1"},
760},
761},
762},
763For: &parser.YamlKeyValue{
764Key: &parser.YamlNode{
765Position: parser.FilePosition{Lines: []int{15}},
766Value: "for",
767},
768Value: &parser.YamlNode{
769Position: parser.FilePosition{Lines: []int{15}},
770Value: "5m",
771},
772},
773Labels: &parser.YamlMap{
774Key: &parser.YamlNode{
775Position: parser.FilePosition{Lines: []int{16}},
776Value: "labels",
777},
778Items: []*parser.YamlKeyValue{
779{
780Key: &parser.YamlNode{
781Position: parser.FilePosition{Lines: []int{17}},
782Value: "priority",
783},
784Value: &parser.YamlNode{
785Position: parser.FilePosition{Lines: []int{17}},
786Value: "2",
787},
788},
789{
790Key: &parser.YamlNode{
791Position: parser.FilePosition{Lines: []int{18}},
792Value: "environment",
793},
794Value: &parser.YamlNode{
795Position: parser.FilePosition{Lines: []int{18}},
796Value: "production",
797},
798},
799},
800},
801Annotations: &parser.YamlMap{
802Key: &parser.YamlNode{
803Position: parser.FilePosition{Lines: []int{19}},
804Value: "annotations",
805},
806Items: []*parser.YamlKeyValue{
807{
808Key: &parser.YamlNode{
809Position: parser.FilePosition{Lines: []int{20}},
810Value: "summary",
811},
812Value: &parser.YamlNode{
813Position: parser.FilePosition{Lines: []int{20}},
814Value: "No replicas for Example have been running for 5 minutes",
815},
816},
817},
818},
819},
820},
821{
822AlertingRule: &parser.AlertingRule{
823Alert: parser.YamlKeyValue{
824Key: &parser.YamlNode{
825Position: parser.FilePosition{Lines: []int{22}},
826Value: "alert",
827},
828Value: &parser.YamlNode{
829Position: parser.FilePosition{Lines: []int{22}},
830Value: "Example_High_Restart_Rate",
831},
832},
833Expr: parser.PromQLExpr{
834Key: &parser.YamlNode{
835Position: parser.FilePosition{Lines: []int{23}},
836Value: "expr",
837},
838Value: &parser.YamlNode{
839Position: parser.FilePosition{Lines: []int{23}},
840Value: `sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )`,
841},
842Query: &parser.PromQLNode{
843Expr: `sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])) > ( 3/60 )`,
844Children: []*parser.PromQLNode{
845{
846Expr: `sum(rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m]))`,
847Children: []*parser.PromQLNode{
848{
849Expr: `rate(kube_pod_container_status_restarts_total{namespace="example-app"}[5m])`,
850Children: []*parser.PromQLNode{
851{
852Expr: `kube_pod_container_status_restarts_total{namespace="example-app"}[5m]`,
853Children: []*parser.PromQLNode{
854{Expr: `kube_pod_container_status_restarts_total{namespace="example-app"}`},
855},
856},
857},
858},
859},
860},
861{
862Expr: "(3 / 60)",
863Children: []*parser.PromQLNode{
864{
865Expr: "3 / 60",
866Children: []*parser.PromQLNode{
867{Expr: "3"},
868{Expr: "60"},
869},
870},
871},
872},
873},
874},
875},
876},
877},
878},
a68c8b60Lukasz Mierzwa5 years ago879},
3e11b598Lukasz Mierzwa4 years ago880{
881content: []byte(`groups:
882- name: "haproxy.api_server.rules"
883rules:
884- alert: HaproxyServerHealthcheckFailure
885expr: increase(haproxy_server_check_failures_total[15m]) > 100
886for: 5m
887labels:
888severity: 24x7
889annotations:
890summary: "HAProxy server healthcheck failure (instance {{ $labels.instance }})"
891description: "Some server healthcheck are failing on {{ $labels.server }}\n VALUE = {{ $value }}\n LABELS: {{ $labels }}"
892`),
893output: []parser.Rule{
894{
895AlertingRule: &parser.AlertingRule{
896Alert: parser.YamlKeyValue{
897Key: &parser.YamlNode{
898Position: parser.FilePosition{Lines: []int{4}},
899Value: "alert",
900},
901Value: &parser.YamlNode{
902Position: parser.FilePosition{Lines: []int{4}},
903Value: "HaproxyServerHealthcheckFailure",
904},
905},
906Expr: parser.PromQLExpr{
907Key: &parser.YamlNode{
908Position: parser.FilePosition{Lines: []int{5}},
909Value: "expr",
910},
911Value: &parser.YamlNode{
912Position: parser.FilePosition{Lines: []int{5}},
913Value: "increase(haproxy_server_check_failures_total[15m]) > 100",
914},
915Query: &parser.PromQLNode{
916Expr: "increase(haproxy_server_check_failures_total[15m]) > 100",
917Children: []*parser.PromQLNode{
918{
919Expr: "increase(haproxy_server_check_failures_total[15m])",
920Children: []*parser.PromQLNode{
921{
922Expr: "haproxy_server_check_failures_total[15m]",
923Children: []*parser.PromQLNode{
924{
925Expr: "haproxy_server_check_failures_total",
3cc8a753Lukasz Mierzwa5 years ago926},
927},
928},
929},
930},
3e11b598Lukasz Mierzwa4 years ago931{Expr: "100"},
932},
933},
934},
935For: &parser.YamlKeyValue{
936Key: &parser.YamlNode{
937Position: parser.FilePosition{Lines: []int{6}},
938Value: "for",
939},
940Value: &parser.YamlNode{
941Position: parser.FilePosition{Lines: []int{6}},
942Value: "5m",
943},
944},
945Labels: &parser.YamlMap{
946Key: &parser.YamlNode{
947Position: parser.FilePosition{Lines: []int{7}},
948Value: "labels",
949},
950Items: []*parser.YamlKeyValue{
951{
952Key: &parser.YamlNode{
953Position: parser.FilePosition{Lines: []int{8}},
954Value: "severity",
3cc8a753Lukasz Mierzwa5 years ago955},
3e11b598Lukasz Mierzwa4 years ago956Value: &parser.YamlNode{
957Position: parser.FilePosition{Lines: []int{8}},
958Value: "24x7",
3cc8a753Lukasz Mierzwa5 years ago959},
3e11b598Lukasz Mierzwa4 years ago960},
961},
962},
963Annotations: &parser.YamlMap{
964Key: &parser.YamlNode{
965Position: parser.FilePosition{Lines: []int{9}},
966Value: "annotations",
967},
968Items: []*parser.YamlKeyValue{
969{
970Key: &parser.YamlNode{
971Position: parser.FilePosition{Lines: []int{10}},
972Value: "summary",
973},
974Value: &parser.YamlNode{
975Position: parser.FilePosition{Lines: []int{10}},
976Value: "HAProxy server healthcheck failure (instance {{ $labels.instance }})",
977},
978},
979{
980Key: &parser.YamlNode{
981Position: parser.FilePosition{Lines: []int{11}},
982Value: "description",
983},
984Value: &parser.YamlNode{
985// FIXME https://github.com/cloudflare/pint/issues/20
986// Should be Lines: [11]
987Position: parser.FilePosition{Lines: []int{11, 12, 13}},
988// Should be `Some ...` since \n should be escaped
989Value: "Some server healthcheck are failing on {{ $labels.server }}\n VALUE = {{ $value }}\n LABELS: {{ $labels }}",
990},
991},
992},
993},
994},
995},
996},
997},
998{
999content: []byte(`groups:
1000- name: certmanager
1001rules:
1002- &recordAnchor
1003record: name1
1004expr: expr1
1005- <<: *recordAnchor
1006expr: expr2
1007- <<: *recordAnchor
1008`),
1009output: []parser.Rule{
1010{
1011RecordingRule: &parser.RecordingRule{
1012Record: parser.YamlKeyValue{
1013Key: &parser.YamlNode{
1014Position: parser.FilePosition{Lines: []int{5}},
1015Value: "record",
1016},
1017Value: &parser.YamlNode{
1018Position: parser.FilePosition{Lines: []int{5}},
1019Value: "name1",
1020},
1021},
1022Expr: parser.PromQLExpr{
1023Key: &parser.YamlNode{
1024Position: parser.FilePosition{Lines: []int{6}},
1025Value: "expr",
1026},
1027Value: &parser.YamlNode{
1028Position: parser.FilePosition{Lines: []int{6}},
1029Value: "expr1",
1030},
1031Query: &parser.PromQLNode{Expr: "expr1"},
1032},
1033},
1034},
1035{
1036RecordingRule: &parser.RecordingRule{
1037Record: parser.YamlKeyValue{
1038Key: &parser.YamlNode{
1039Position: parser.FilePosition{Lines: []int{5}},
1040Value: "record",
1041},
1042Value: &parser.YamlNode{
1043Position: parser.FilePosition{Lines: []int{5}},
1044Value: "name1",
1045},
1046},
1047Expr: parser.PromQLExpr{
1048Key: &parser.YamlNode{
1049Position: parser.FilePosition{Lines: []int{8}},
1050Value: "expr",
1051},
1052Value: &parser.YamlNode{
1053Position: parser.FilePosition{Lines: []int{8}},
1054Value: "expr2",
1055},
1056Query: &parser.PromQLNode{Expr: "expr2"},
1057},
1058},
1059},
1060{
1061RecordingRule: &parser.RecordingRule{
1062Record: parser.YamlKeyValue{
1063Key: &parser.YamlNode{
1064Position: parser.FilePosition{Lines: []int{5}},
1065Value: "record",
1066},
1067Value: &parser.YamlNode{
1068Position: parser.FilePosition{Lines: []int{5}},
1069Value: "name1",
1070},
1071},
1072Expr: parser.PromQLExpr{
1073Key: &parser.YamlNode{
1074Position: parser.FilePosition{Lines: []int{6}},
1075Value: "expr",
1076},
1077Value: &parser.YamlNode{
1078Position: parser.FilePosition{Lines: []int{6}},
1079Value: "expr1",
1080},
1081Query: &parser.PromQLNode{Expr: "expr1"},
1082},
1083},
1084},
1085},
1086},
1087{
1088content: []byte(`groups:
1089- name: certmanager
1090rules:
1091- record: name1
1092expr: expr1
1093labels: &labelsAnchor
1094label1: val1
1095label2: val2
1096- record: name2
1097expr: expr2
1098labels: *labelsAnchor
1099# foot comment
1100`),
1101output: []parser.Rule{
1102{
1103RecordingRule: &parser.RecordingRule{
1104Record: parser.YamlKeyValue{
1105Key: &parser.YamlNode{
1106Position: parser.FilePosition{Lines: []int{4}},
1107Value: "record",
1108},
1109Value: &parser.YamlNode{
1110Position: parser.FilePosition{Lines: []int{4}},
1111Value: "name1",
1112},
1113},
1114Expr: parser.PromQLExpr{
1115Key: &parser.YamlNode{
1116Position: parser.FilePosition{Lines: []int{5}},
1117Value: "expr",
1118},
1119Value: &parser.YamlNode{
1120Position: parser.FilePosition{Lines: []int{5}},
1121Value: "expr1",
1122},
1123Query: &parser.PromQLNode{Expr: "expr1"},
1124},
1125Labels: &parser.YamlMap{
1126Key: &parser.YamlNode{
1127Position: parser.FilePosition{Lines: []int{6}},
1128Value: "labels",
1129},
1130Items: []*parser.YamlKeyValue{
1131{
1132Key: &parser.YamlNode{
1133Position: parser.FilePosition{Lines: []int{7}},
1134Value: "label1",
1135},
1136Value: &parser.YamlNode{
1137Position: parser.FilePosition{Lines: []int{7}},
1138Value: "val1",
1139},
1140},
1141{
1142Key: &parser.YamlNode{
1143Position: parser.FilePosition{Lines: []int{8}},
1144Value: "label2",
1145},
1146Value: &parser.YamlNode{
1147Position: parser.FilePosition{Lines: []int{8}},
1148Value: "val2",
1149},
1150},
1151},
1152},
1153},
1154},
1155{
1156RecordingRule: &parser.RecordingRule{
1157Record: parser.YamlKeyValue{
1158Key: &parser.YamlNode{
1159Position: parser.FilePosition{Lines: []int{9}},
1160Value: "record",
1161},
1162Value: &parser.YamlNode{
1163Position: parser.FilePosition{Lines: []int{9}},
1164Value: "name2",
1165},
1166},
1167Expr: parser.PromQLExpr{
1168Key: &parser.YamlNode{
1169Position: parser.FilePosition{Lines: []int{10}},
1170Value: "expr",
1171},
1172Value: &parser.YamlNode{
1173Position: parser.FilePosition{Lines: []int{10}},
1174Value: "expr2",
1175},
1176Query: &parser.PromQLNode{Expr: "expr2"},
1177},
1178Labels: &parser.YamlMap{
1179Key: &parser.YamlNode{
1180Position: parser.FilePosition{Lines: []int{11}},
1181Value: "labels",
1182Comments: []string{"# foot comment"},
1183},
1184Items: []*parser.YamlKeyValue{
1185{
1186Key: &parser.YamlNode{
1187Position: parser.FilePosition{Lines: []int{7}},
1188Value: "label1",
1189},
1190Value: &parser.YamlNode{
1191Position: parser.FilePosition{Lines: []int{7}},
1192Value: "val1",
1193},
1194},
1195{
1196Key: &parser.YamlNode{
1197Position: parser.FilePosition{Lines: []int{8}},
1198Value: "label2",
1199},
1200Value: &parser.YamlNode{
1201Position: parser.FilePosition{Lines: []int{8}},
1202Value: "val2",
3cc8a753Lukasz Mierzwa5 years ago1203},
1204},
1205},
1206},
1207},
3e11b598Lukasz Mierzwa4 years ago1208},
1209},
1210},
a68c8b60Lukasz Mierzwa5 years ago1211}
1212
1213alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true })
1214ignorePrometheusExpr := cmp.FilterValues(func(x, y interface{}) bool {
1215_, xe := x.(promparser.Expr)
1216_, ye := y.(promparser.Expr)
1217return xe || ye
1218}, alwaysEqual)
1219
1220cmpErrorText := cmp.Comparer(func(x, y interface{}) bool {
1221xe := x.(error)
1222ye := y.(error)
1223return xe.Error() == ye.Error()
1224})
1225sameErrorText := cmp.FilterValues(func(x, y interface{}) bool {
1226_, xe := x.(error)
1227_, ye := y.(error)
1228return xe && ye
1229}, cmpErrorText)
1230
1231for i, tc := range testCases {
1232t.Run(strconv.Itoa(i+1), func(t *testing.T) {
1233p := parser.NewParser()
1234output, err := p.Parse(tc.content)
1235
1236hadError := err != nil
1237if hadError != tc.shouldError {
1238t.Errorf("Parse() returned err=%v, expected=%v", err, tc.shouldError)
1239return
1240}
1241
1242if diff := cmp.Diff(tc.output, output, ignorePrometheusExpr, sameErrorText); diff != "" {
1243t.Errorf("Parse() returned wrong output (-want +got):\n%s", diff)
1244return
1245}
1246})
1247}
1248}