cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.17.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/parser.go

259lines · modeblame

a68c8b60Lukasz Mierzwa5 years ago1package parser
2
3import (
4"fmt"
5"strings"
6
7"gopkg.in/yaml.v3"
8)
9
10const (
11recordKey = "record"
12exprKey = "expr"
13labelsKey = "labels"
14alertKey = "alert"
15forKey = "for"
16annotationsKey = "annotations"
17)
18
19func NewParser() Parser {
20return Parser{}
21}
22
23type Parser struct{}
24
25func (p Parser) Parse(content []byte) (rules []Rule, err error) {
26if len(content) == 0 {
27return
28}
29
7d1ea938Lukasz Mierzwa4 years ago30defer func() {
31if r := recover(); r != nil {
32err = fmt.Errorf("unable to parse YAML file: %s", r)
33}
34}()
35
a68c8b60Lukasz Mierzwa5 years ago36var node yaml.Node
37err = yaml.Unmarshal(content, &node)
38if err != nil {
39return nil, err
40}
41
42return parseNode(content, &node)
43}
44
45func parseNode(content []byte, node *yaml.Node) (rules []Rule, err error) {
46ret, isEmpty, err := parseRule(content, node)
47if err != nil {
48return nil, err
49}
50if !isEmpty {
51rules = append(rules, ret)
52return
53}
54
55for _, root := range node.Content {
56switch root.Kind {
57case yaml.SequenceNode:
58for _, n := range root.Content {
59ret, err := parseNode(content, n)
60if err != nil {
61return nil, err
62}
63rules = append(rules, ret...)
64}
65case yaml.MappingNode:
66rule, isEmpty, err := parseRule(content, root)
67if err != nil {
68return nil, err
69}
70if !isEmpty {
71rules = append(rules, rule)
72} else {
73for _, n := range root.Content {
74ret, err := parseNode(content, n)
75if err != nil {
76return nil, err
77}
78rules = append(rules, ret...)
79}
80}
81}
82}
83return rules, nil
84}
85
86func parseRule(content []byte, node *yaml.Node) (rule Rule, isEmpty bool, err error) {
87isEmpty = true
88
89if node.Kind != yaml.MappingNode {
90return
91}
92
93var recordPart *YamlKeyValue
94var exprPart *PromQLExpr
95var labelsPart *YamlMap
96
97var alertPart *YamlKeyValue
98var forPart *YamlKeyValue
99var annotationsPart *YamlMap
100
101var key *yaml.Node
102unknownKeys := []*yaml.Node{}
103for i, part := range node.Content {
032875e9Lukasz Mierzwa5 years ago104if i == 0 && node.HeadComment != "" {
105part.HeadComment = node.HeadComment
106}
107if i == len(node.Content)-1 && node.FootComment != "" {
108part.FootComment = node.FootComment
109}
a68c8b60Lukasz Mierzwa5 years ago110if i%2 == 0 {
111key = part
112} else {
113switch key.Value {
114case recordKey:
14ab07ffLukasz Mierzwa4 years ago115if recordPart != nil {
58ae7de1Lukasz Mierzwa4 years ago116return duplicatedKeyError(part.Line, recordKey, nil)
14ab07ffLukasz Mierzwa4 years ago117}
a68c8b60Lukasz Mierzwa5 years ago118recordPart = newYamlKeyValue(key, part)
119case alertKey:
14ab07ffLukasz Mierzwa4 years ago120if alertPart != nil {
58ae7de1Lukasz Mierzwa4 years ago121return duplicatedKeyError(part.Line, alertKey, nil)
14ab07ffLukasz Mierzwa4 years ago122}
a68c8b60Lukasz Mierzwa5 years ago123alertPart = newYamlKeyValue(key, part)
124case exprKey:
14ab07ffLukasz Mierzwa4 years ago125if exprPart != nil {
58ae7de1Lukasz Mierzwa4 years ago126return duplicatedKeyError(part.Line, exprKey, nil)
14ab07ffLukasz Mierzwa4 years ago127}
a68c8b60Lukasz Mierzwa5 years ago128exprPart = newPromQLExpr(key, part)
129case forKey:
14ab07ffLukasz Mierzwa4 years ago130if forPart != nil {
58ae7de1Lukasz Mierzwa4 years ago131return duplicatedKeyError(part.Line, forKey, nil)
14ab07ffLukasz Mierzwa4 years ago132}
a68c8b60Lukasz Mierzwa5 years ago133forPart = newYamlKeyValue(key, part)
134case labelsKey:
14ab07ffLukasz Mierzwa4 years ago135if labelsPart != nil {
58ae7de1Lukasz Mierzwa4 years ago136return duplicatedKeyError(part.Line, labelsKey, nil)
14ab07ffLukasz Mierzwa4 years ago137}
a68c8b60Lukasz Mierzwa5 years ago138labelsPart = newYamlMap(key, part)
139case annotationsKey:
14ab07ffLukasz Mierzwa4 years ago140if annotationsPart != nil {
58ae7de1Lukasz Mierzwa4 years ago141return duplicatedKeyError(part.Line, annotationsKey, nil)
14ab07ffLukasz Mierzwa4 years ago142}
a68c8b60Lukasz Mierzwa5 years ago143annotationsPart = newYamlMap(key, part)
144default:
145unknownKeys = append(unknownKeys, key)
146}
147}
148}
149
3cc8a753Lukasz Mierzwa5 years ago150if exprPart != nil && exprPart.Key.Position.FirstLine() != exprPart.Value.Position.FirstLine() {
a68c8b60Lukasz Mierzwa5 years ago151for {
3cc8a753Lukasz Mierzwa5 years ago152start := exprPart.Value.Position.FirstLine() - 1
a68c8b60Lukasz Mierzwa5 years ago153end := exprPart.Value.Position.LastLine()
7d1ea938Lukasz Mierzwa4 years ago154if end > len(strings.Split(string(content), "\n")) {
155end--
156}
a68c8b60Lukasz Mierzwa5 years ago157input := strings.Join(strings.Split(string(content), "\n")[start:end], "")
158input = strings.ReplaceAll(input, " ", "")
159output := strings.ReplaceAll(exprPart.Value.Value, "\n", "")
160output = strings.ReplaceAll(output, " ", "")
161if end >= len(strings.Split(string(content), "\n")) {
162break
163}
164if input == output {
165break
166}
167exprPart.Value.Position.Lines = append(exprPart.Value.Position.Lines, end+1)
168}
169}
170
171if recordPart != nil && alertPart != nil {
172isEmpty = false
173rule = Rule{
174Error: ParseError{
175Line: node.Line,
176Err: fmt.Errorf("got both %s and %s keys in a single rule", recordKey, alertKey),
177},
178}
179return
180}
181if recordPart != nil && exprPart == nil {
182isEmpty = false
183rule = Rule{
184Error: ParseError{
185Line: recordPart.Key.Position.LastLine(),
186Err: fmt.Errorf("missing %s key", exprKey),
187},
188}
189return
190}
191if alertPart != nil && exprPart == nil {
192isEmpty = false
193rule = Rule{
194Error: ParseError{
195Line: alertPart.Key.Position.LastLine(),
196Err: fmt.Errorf("missing %s key", exprKey),
197},
198}
199return
200}
201if exprPart != nil && alertPart == nil && recordPart == nil {
202isEmpty = false
203rule = Rule{
204Error: ParseError{
205Line: exprPart.Key.Position.LastLine(),
206Err: fmt.Errorf("incomplete rule, no %s or %s key", alertKey, recordKey),
207},
208}
209return
210}
211if (recordPart != nil || alertPart != nil) && len(unknownKeys) > 0 {
212isEmpty = false
213var keys []string
214for _, n := range unknownKeys {
215keys = append(keys, n.Value)
216}
217rule = Rule{
218Error: ParseError{
219Line: unknownKeys[0].Line,
220Err: fmt.Errorf("invalid key(s) found: %s", strings.Join(keys, ", ")),
221},
222}
223return
224}
225
226if recordPart != nil && exprPart != nil {
227isEmpty = false
228rule = Rule{RecordingRule: &RecordingRule{
229Record: *recordPart,
230Expr: *exprPart,
231Labels: labelsPart,
232}}
233return
234}
235
236if alertPart != nil && exprPart != nil {
237isEmpty = false
238rule = Rule{AlertingRule: &AlertingRule{
239Alert: *alertPart,
240Expr: *exprPart,
241For: forPart,
242Labels: labelsPart,
243Annotations: annotationsPart,
244}}
245return
246}
247
248return
249}
14ab07ffLukasz Mierzwa4 years ago250
251func duplicatedKeyError(line int, key string, err error) (Rule, bool, error) {
252rule := Rule{
253Error: ParseError{
254Line: line,
255Err: fmt.Errorf("duplicated %s key", key),
256},
257}
258return rule, false, err
259}