cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.20.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/parser.go

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