cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.32.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/parser.go

338lines · 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 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
53 }
54
55 for _, root := range node.Content {
56 // nolint: exhaustive
57 switch root.Kind {
58 case yaml.SequenceNode:
59 for _, n := range root.Content {
60 ret, err := parseNode(content, n, offset)
61 if err != nil {
62 return nil, err
63 }
64 rules = append(rules, ret...)
65 }
66 case yaml.MappingNode:
67 rule, isEmpty, err := parseRule(content, root, offset)
68 if err != nil {
69 return nil, err
70 }
71 if !isEmpty {
72 rules = append(rules, rule)
73 } else {
74 for _, n := range root.Content {
75 ret, err := parseNode(content, n, offset)
76 if err != nil {
77 return nil, err
78 }
79 rules = append(rules, ret...)
80 }
81 }
82 case yaml.ScalarNode:
83 if root.Value != string(content) {
84 c := []byte(root.Value)
85 var n yaml.Node
86 err = yaml.Unmarshal(c, &n)
87 if err == nil {
88 ret, err := parseNode(c, &n, offset+root.Line)
89 if err != nil {
90 return nil, err
91 }
92 rules = append(rules, ret...)
93 }
94 }
95 }
96 }
97 return rules, nil
98}
99
100func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, isEmpty bool, err error) {
101 isEmpty = true
102
103 if node.Kind != yaml.MappingNode {
104 return
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 isEmpty = false
188 rule = Rule{
189 Error: ParseError{
190 Line: node.Line + offset,
191 Err: fmt.Errorf("got both %s and %s keys in a single rule", recordKey, alertKey),
192 },
193 }
194 return
195 }
196 if recordPart != nil && exprPart == nil {
197 isEmpty = false
198 rule = Rule{
199 Error: ParseError{
200 Line: recordPart.Key.Position.LastLine(),
201 Err: fmt.Errorf("missing %s key", exprKey),
202 },
203 }
204 return
205 }
206 if alertPart != nil && exprPart == nil {
207 isEmpty = false
208 rule = Rule{
209 Error: ParseError{
210 Line: alertPart.Key.Position.LastLine(),
211 Err: fmt.Errorf("missing %s key", exprKey),
212 },
213 }
214 return
215 }
216 if exprPart != nil && alertPart == nil && recordPart == nil {
217 isEmpty = false
218 rule = Rule{
219 Error: ParseError{
220 Line: exprPart.Key.Position.LastLine(),
221 Err: fmt.Errorf("incomplete rule, no %s or %s key", alertKey, recordKey),
222 },
223 }
224 return
225 }
226 if (recordPart != nil || alertPart != nil) && len(unknownKeys) > 0 {
227 isEmpty = false
228 var keys []string
229 for _, n := range unknownKeys {
230 keys = append(keys, n.Value)
231 }
232 rule = Rule{
233 Error: ParseError{
234 Line: unknownKeys[0].Line + offset,
235 Err: fmt.Errorf("invalid key(s) found: %s", strings.Join(keys, ", ")),
236 },
237 }
238 return
239 }
240
241 if recordPart != nil && exprPart != nil {
242 isEmpty = false
243 rule = Rule{RecordingRule: &RecordingRule{
244 Record: *recordPart,
245 Expr: *exprPart,
246 Labels: labelsPart,
247 }}
248 return
249 }
250
251 if alertPart != nil && exprPart != nil {
252 isEmpty = false
253 rule = Rule{AlertingRule: &AlertingRule{
254 Alert: *alertPart,
255 Expr: *exprPart,
256 For: forPart,
257 Labels: labelsPart,
258 Annotations: annotationsPart,
259 }}
260 return
261 }
262
263 return
264}
265
266func unpackNodes(node *yaml.Node) []*yaml.Node {
267 nodes := make([]*yaml.Node, 0, len(node.Content))
268 var isMerge bool
269 for _, part := range node.Content {
270 if part.Tag == "!!merge" && part.Value == "<<" {
271 isMerge = true
272 }
273
274 if part.Alias != nil {
275 if isMerge {
276 nodes = append(nodes, resolveMapAlias(part, node).Content...)
277 } else {
278 nodes = append(nodes, resolveMapAlias(part, part))
279 }
280 isMerge = false
281 continue
282 }
283 if isMerge {
284 continue
285 }
286 nodes = append(nodes, part)
287 }
288 return nodes
289}
290
291func nodeKeys(node *yaml.Node) (keys []string) {
292 if node.Kind != yaml.MappingNode {
293 return keys
294 }
295 for i, n := range node.Content {
296 if i%2 == 0 && n.Value != "" {
297 keys = append(keys, n.Value)
298 }
299 }
300 return keys
301}
302
303func hasKey(node *yaml.Node, key string) bool {
304 for _, k := range nodeKeys(node) {
305 if k == key {
306 return true
307 }
308 }
309 return false
310}
311
312func resolveMapAlias(part, parent *yaml.Node) *yaml.Node {
313 node := *part
314 node.Content = nil
315 var ok bool
316 for i, alias := range part.Alias.Content {
317 if i%2 == 0 {
318 ok = !hasKey(parent, alias.Value)
319 }
320 if ok {
321 node.Content = append(node.Content, alias)
322 }
323 if i%2 == 1 {
324 ok = false
325 }
326 }
327 return &node
328}
329
330func duplicatedKeyError(line int, key string, err error) (Rule, bool, error) {
331 rule := Rule{
332 Error: ParseError{
333 Line: line,
334 Err: fmt.Errorf("duplicated %s key", key),
335 },
336 }
337 return rule, false, err
338}
339