cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1cddaa3de498d034aeb8affdea9bb89f8378c40b

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/parser.go

357lines · 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 nil, nil
28 }
29
30 defer func() {
31 if r := recover(); r != nil {
32 err = fmt.Errorf("unable to parse YAML file: %s", r)
33 }
34 }()
35
36 var node yaml.Node
37 err = yaml.Unmarshal(content, &node)
38 if err != nil {
39 return nil, err
40 }
41
42 return parseNode(content, &node, 0)
43}
44
45func parseNode(content []byte, node *yaml.Node, offset int) (rules []Rule, err error) {
46 ret, isEmpty, err := parseRule(content, node, offset)
47 if err != nil {
48 return nil, err
49 }
50 if !isEmpty {
51 rules = append(rules, ret)
52 return rules, nil
53 }
54
55 var rl []Rule
56 var rule Rule
57 for _, root := range node.Content {
58 // nolint: exhaustive
59 switch root.Kind {
60 case yaml.SequenceNode:
61 for _, n := range root.Content {
62 rl, err = parseNode(content, n, offset)
63 if err != nil {
64 return nil, err
65 }
66 rules = append(rules, rl...)
67 }
68 case yaml.MappingNode:
69 rule, isEmpty, err = parseRule(content, root, offset)
70 if err != nil {
71 return nil, err
72 }
73 if !isEmpty {
74 rules = append(rules, rule)
75 } else {
76 for _, n := range root.Content {
77 rl, err = parseNode(content, n, offset)
78 if err != nil {
79 return nil, err
80 }
81 rules = append(rules, rl...)
82 }
83 }
84 case yaml.ScalarNode:
85 if root.Value != string(content) {
86 c := []byte(root.Value)
87 var n yaml.Node
88 err = yaml.Unmarshal(c, &n)
89 if err == nil {
90 ret, err := parseNode(c, &n, offset+root.Line)
91 if err != nil {
92 return nil, err
93 }
94 rules = append(rules, ret...)
95 }
96 }
97 }
98 }
99 return rules, nil
100}
101
102func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool, err error) {
103 if node.Kind != yaml.MappingNode {
104 return rule, true, err
105 }
106
107 var recordPart *YamlKeyValue
108 var exprPart *PromQLExpr
109 var labelsPart *YamlMap
110
111 var alertPart *YamlKeyValue
112 var forPart *YamlKeyValue
113 var annotationsPart *YamlMap
114
115 var key *yaml.Node
116 unknownKeys := []*yaml.Node{}
117
118 for i, part := range unpackNodes(node) {
119 if i == 0 && node.HeadComment != "" {
120 part.HeadComment = node.HeadComment
121 }
122 if i == len(node.Content)-1 && node.FootComment != "" {
123 part.FootComment = node.FootComment
124 }
125 if i%2 == 0 {
126 key = part
127 } else {
128 switch key.Value {
129 case recordKey:
130 if recordPart != nil {
131 return duplicatedKeyError(part.Line+offset, recordKey, nil)
132 }
133 recordPart = newYamlKeyValue(key, part, offset)
134 case alertKey:
135 if alertPart != nil {
136 return duplicatedKeyError(part.Line+offset, alertKey, nil)
137 }
138 alertPart = newYamlKeyValue(key, part, offset)
139 case exprKey:
140 if exprPart != nil {
141 return duplicatedKeyError(part.Line+offset, exprKey, nil)
142 }
143 exprPart = newPromQLExpr(key, part, offset)
144 case forKey:
145 if forPart != nil {
146 return duplicatedKeyError(part.Line+offset, forKey, nil)
147 }
148 forPart = newYamlKeyValue(key, part, offset)
149 case labelsKey:
150 if labelsPart != nil {
151 return duplicatedKeyError(part.Line+offset, labelsKey, nil)
152 }
153 labelsPart = newYamlMap(key, part, offset)
154 case annotationsKey:
155 if annotationsPart != nil {
156 return duplicatedKeyError(part.Line+offset, annotationsKey, nil)
157 }
158 annotationsPart = newYamlMap(key, part, offset)
159 default:
160 unknownKeys = append(unknownKeys, key)
161 }
162 }
163 }
164
165 if exprPart != nil && exprPart.Key.Position.FirstLine() != exprPart.Value.Position.FirstLine() {
166 for {
167 start := exprPart.Value.Position.FirstLine() - 1
168 end := exprPart.Value.Position.LastLine()
169 if end > len(strings.Split(string(content), "\n")) {
170 end--
171 }
172 input := strings.Join(strings.Split(string(content), "\n")[start:end], "")
173 input = strings.ReplaceAll(input, " ", "")
174 output := strings.ReplaceAll(exprPart.Value.Value, "\n", "")
175 output = strings.ReplaceAll(output, " ", "")
176 if end >= len(strings.Split(string(content), "\n")) {
177 break
178 }
179 if input == output {
180 break
181 }
182 exprPart.Value.Position.Lines = append(exprPart.Value.Position.Lines, end+1)
183 }
184 }
185
186 if recordPart != nil && alertPart != nil {
187 rule = Rule{
188 Error: ParseError{
189 Line: node.Line + offset,
190 Err: fmt.Errorf("got both %s and %s keys in a single rule", recordKey, alertKey),
191 },
192 }
193 return rule, false, err
194 }
195 if exprPart != nil && alertPart == nil && recordPart == nil {
196 rule = Rule{
197 Error: ParseError{
198 Line: exprPart.Key.Position.LastLine(),
199 Err: fmt.Errorf("incomplete rule, no %s or %s key", alertKey, recordKey),
200 },
201 }
202 return rule, false, err
203 }
204 if r, ok := ensureRequiredKeys(recordPart, exprPart); !ok {
205 return r, false, err
206 }
207 if r, ok := ensureRequiredKeys(alertPart, exprPart); !ok {
208 return r, false, err
209 }
210 if (recordPart != nil || alertPart != nil) && len(unknownKeys) > 0 {
211 var keys []string
212 for _, n := range unknownKeys {
213 keys = append(keys, n.Value)
214 }
215 rule = Rule{
216 Error: ParseError{
217 Line: unknownKeys[0].Line + offset,
218 Err: fmt.Errorf("invalid key(s) found: %s", strings.Join(keys, ", ")),
219 },
220 }
221 return rule, false, err
222 }
223
224 if recordPart != nil && exprPart != nil {
225 rule = Rule{RecordingRule: &RecordingRule{
226 Record: *recordPart,
227 Expr: *exprPart,
228 Labels: labelsPart,
229 }}
230 return rule, false, err
231 }
232
233 if alertPart != nil && exprPart != nil {
234 rule = Rule{AlertingRule: &AlertingRule{
235 Alert: *alertPart,
236 Expr: *exprPart,
237 For: forPart,
238 Labels: labelsPart,
239 Annotations: annotationsPart,
240 }}
241 return rule, false, err
242 }
243
244 return rule, true, err
245}
246
247func unpackNodes(node *yaml.Node) []*yaml.Node {
248 nodes := make([]*yaml.Node, 0, len(node.Content))
249 var isMerge bool
250 for _, part := range node.Content {
251 if part.Tag == "!!merge" && part.Value == "<<" {
252 isMerge = true
253 }
254
255 if part.Alias != nil {
256 if isMerge {
257 nodes = append(nodes, resolveMapAlias(part, node).Content...)
258 } else {
259 nodes = append(nodes, resolveMapAlias(part, part))
260 }
261 isMerge = false
262 continue
263 }
264 if isMerge {
265 continue
266 }
267 nodes = append(nodes, part)
268 }
269 return nodes
270}
271
272func nodeKeys(node *yaml.Node) (keys []string) {
273 if node.Kind != yaml.MappingNode {
274 return keys
275 }
276 for i, n := range node.Content {
277 if i%2 == 0 && n.Value != "" {
278 keys = append(keys, n.Value)
279 }
280 }
281 return keys
282}
283
284func hasKey(node *yaml.Node, key string) bool {
285 for _, k := range nodeKeys(node) {
286 if k == key {
287 return true
288 }
289 }
290 return false
291}
292
293func hasValue(node *YamlNode) bool {
294 if node == nil {
295 return false
296 }
297 return node.Value != ""
298}
299
300func ensureRequiredKeys(key *YamlKeyValue, expr *PromQLExpr) (Rule, bool) {
301 if key == nil {
302 return Rule{}, true
303 }
304 if !hasValue(key.Value) {
305 return Rule{
306 Error: ParseError{
307 Line: key.Value.Position.LastLine(),
308 Err: fmt.Errorf("%s value cannot be empty", key.Key.Value),
309 },
310 }, false
311 }
312 if expr == nil {
313 return Rule{
314 Error: ParseError{
315 Line: key.Value.Position.LastLine(),
316 Err: fmt.Errorf("missing %s key", exprKey),
317 },
318 }, false
319 }
320 if !hasValue(expr.Value) {
321 return Rule{
322 Error: ParseError{
323 Line: expr.Value.Position.LastLine(),
324 Err: fmt.Errorf("%s value cannot be empty", exprKey),
325 },
326 }, false
327 }
328 return Rule{}, true
329}
330
331func resolveMapAlias(part, parent *yaml.Node) *yaml.Node {
332 node := *part
333 node.Content = nil
334 var ok bool
335 for i, alias := range part.Alias.Content {
336 if i%2 == 0 {
337 ok = !hasKey(parent, alias.Value)
338 }
339 if ok {
340 node.Content = append(node.Content, alias)
341 }
342 if i%2 == 1 {
343 ok = false
344 }
345 }
346 return &node
347}
348
349func duplicatedKeyError(line int, key string, err error) (Rule, bool, error) {
350 rule := Rule{
351 Error: ParseError{
352 Line: line,
353 Err: fmt.Errorf("duplicated %s key", key),
354 },
355 }
356 return rule, false, err
357}
358