cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
57cfd439d6bd9430fa05bf490e6a16ff6d9d3279

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/parser.go

216lines · modecode

1package parser
2
3import (
4 "fmt"
5 "strings"
6
7 "gopkg.in/yaml.v3"
8)
9
10const (
11 recordKey = "record"
12 exprKey = "expr"
13 labelsKey = "labels"
14 alertKey = "alert"
15 forKey = "for"
16 annotationsKey = "annotations"
17)
18
19func NewParser() Parser {
20 return Parser{}
21}
22
23type Parser struct{}
24
25func (p Parser) Parse(content []byte) (rules []Rule, err error) {
26 if len(content) == 0 {
27 return
28 }
29
30 var node yaml.Node
31 err = yaml.Unmarshal(content, &node)
32 if err != nil {
33 return nil, err
34 }
35
36 return parseNode(content, &node)
37}
38
39func parseNode(content []byte, node *yaml.Node) (rules []Rule, err error) {
40 ret, isEmpty, err := parseRule(content, node)
41 if err != nil {
42 return nil, err
43 }
44 if !isEmpty {
45 rules = append(rules, ret)
46 return
47 }
48
49 for _, root := range node.Content {
50 switch root.Kind {
51 case yaml.SequenceNode:
52 for _, n := range root.Content {
53 ret, err := parseNode(content, n)
54 if err != nil {
55 return nil, err
56 }
57 rules = append(rules, ret...)
58 }
59 case yaml.MappingNode:
60 rule, isEmpty, err := parseRule(content, root)
61 if err != nil {
62 return nil, err
63 }
64 if !isEmpty {
65 rules = append(rules, rule)
66 } else {
67 for _, n := range root.Content {
68 ret, err := parseNode(content, n)
69 if err != nil {
70 return nil, err
71 }
72 rules = append(rules, ret...)
73 }
74 }
75 }
76 }
77 return rules, nil
78}
79
80func parseRule(content []byte, node *yaml.Node) (rule Rule, isEmpty bool, err error) {
81 isEmpty = true
82
83 if node.Kind != yaml.MappingNode {
84 return
85 }
86
87 var recordPart *YamlKeyValue
88 var exprPart *PromQLExpr
89 var labelsPart *YamlMap
90
91 var alertPart *YamlKeyValue
92 var forPart *YamlKeyValue
93 var annotationsPart *YamlMap
94
95 var key *yaml.Node
96 unknownKeys := []*yaml.Node{}
97 for i, part := range node.Content {
98 if i%2 == 0 {
99 key = part
100 } else {
101 switch key.Value {
102 case recordKey:
103 recordPart = newYamlKeyValue(key, part)
104 case alertKey:
105 alertPart = newYamlKeyValue(key, part)
106 case exprKey:
107 exprPart = newPromQLExpr(key, part)
108 case forKey:
109 forPart = newYamlKeyValue(key, part)
110 case labelsKey:
111 labelsPart = newYamlMap(key, part)
112 case annotationsKey:
113 annotationsPart = newYamlMap(key, part)
114 default:
115 unknownKeys = append(unknownKeys, key)
116 }
117 }
118 }
119
120 if exprPart != nil && exprPart.Key.Position.FistLine() != exprPart.Value.Position.FistLine() {
121 for {
122 start := exprPart.Value.Position.FistLine() - 1
123 end := exprPart.Value.Position.LastLine()
124 input := strings.Join(strings.Split(string(content), "\n")[start:end], "")
125 input = strings.ReplaceAll(input, " ", "")
126 output := strings.ReplaceAll(exprPart.Value.Value, "\n", "")
127 output = strings.ReplaceAll(output, " ", "")
128 if end >= len(strings.Split(string(content), "\n")) {
129 break
130 }
131 if input == output {
132 break
133 }
134 exprPart.Value.Position.Lines = append(exprPart.Value.Position.Lines, end+1)
135 }
136 }
137
138 if recordPart != nil && alertPart != nil {
139 isEmpty = false
140 rule = Rule{
141 Error: ParseError{
142 Line: node.Line,
143 Err: fmt.Errorf("got both %s and %s keys in a single rule", recordKey, alertKey),
144 },
145 }
146 return
147 }
148 if recordPart != nil && exprPart == nil {
149 isEmpty = false
150 rule = Rule{
151 Error: ParseError{
152 Line: recordPart.Key.Position.LastLine(),
153 Err: fmt.Errorf("missing %s key", exprKey),
154 },
155 }
156 return
157 }
158 if alertPart != nil && exprPart == nil {
159 isEmpty = false
160 rule = Rule{
161 Error: ParseError{
162 Line: alertPart.Key.Position.LastLine(),
163 Err: fmt.Errorf("missing %s key", exprKey),
164 },
165 }
166 return
167 }
168 if exprPart != nil && alertPart == nil && recordPart == nil {
169 isEmpty = false
170 rule = Rule{
171 Error: ParseError{
172 Line: exprPart.Key.Position.LastLine(),
173 Err: fmt.Errorf("incomplete rule, no %s or %s key", alertKey, recordKey),
174 },
175 }
176 return
177 }
178 if (recordPart != nil || alertPart != nil) && len(unknownKeys) > 0 {
179 isEmpty = false
180 var keys []string
181 for _, n := range unknownKeys {
182 keys = append(keys, n.Value)
183 }
184 rule = Rule{
185 Error: ParseError{
186 Line: unknownKeys[0].Line,
187 Err: fmt.Errorf("invalid key(s) found: %s", strings.Join(keys, ", ")),
188 },
189 }
190 return
191 }
192
193 if recordPart != nil && exprPart != nil {
194 isEmpty = false
195 rule = Rule{RecordingRule: &RecordingRule{
196 Record: *recordPart,
197 Expr: *exprPart,
198 Labels: labelsPart,
199 }}
200 return
201 }
202
203 if alertPart != nil && exprPart != nil {
204 isEmpty = false
205 rule = Rule{AlertingRule: &AlertingRule{
206 Alert: *alertPart,
207 Expr: *exprPart,
208 For: forPart,
209 Labels: labelsPart,
210 Annotations: annotationsPart,
211 }}
212 return
213 }
214
215 return
216}
217