cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.42.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

540lines · modecode

1package parser
2
3import (
4 "bufio"
5 "fmt"
6 "strings"
7
8 "golang.org/x/exp/slices"
9 "gopkg.in/yaml.v3"
10
11 promparser "github.com/prometheus/prometheus/promql/parser"
12)
13
14func appendLine(lines []int, newLines ...int) []int {
15 for _, nl := range newLines {
16 var present bool
17 for _, l := range lines {
18 if l == nl {
19 present = true
20 break
21 }
22 }
23 if !present {
24 lines = append(lines, nl)
25 }
26 }
27
28 return lines
29}
30
31func nodeLines(node *yaml.Node, offset int) (lines []int) {
32 lineCount := len(strings.Split(strings.TrimSuffix(node.Value, "\n"), "\n"))
33
34 var firstLine int
35 // nolint: exhaustive
36 switch node.Style {
37 case yaml.LiteralStyle, yaml.FoldedStyle:
38 firstLine = node.Line + 1 + offset
39 default:
40 firstLine = node.Line + offset
41 }
42
43 for i := 0; i < lineCount; i++ {
44 lines = appendLine(lines, firstLine+i)
45 }
46
47 return lines
48}
49
50func NewFilePosition(l []int) FilePosition {
51 return FilePosition{Lines: l}
52}
53
54type FilePosition struct {
55 Lines []int
56}
57
58func (fp FilePosition) FirstLine() (line int) {
59 for _, l := range fp.Lines {
60 if line == 0 || l < line {
61 line = l
62 }
63 }
64 return line
65}
66
67func (fp FilePosition) LastLine() (line int) {
68 for _, l := range fp.Lines {
69 if l > line {
70 line = l
71 }
72 }
73 return line
74}
75
76func mergeComments(node *yaml.Node) (comments []string) {
77 if node.HeadComment != "" {
78 comments = append(comments, node.HeadComment)
79 }
80 if node.LineComment != "" {
81 comments = append(comments, node.LineComment)
82 }
83 if node.FootComment != "" {
84 comments = append(comments, node.FootComment)
85 }
86 return comments
87}
88
89type YamlNode struct {
90 Position FilePosition
91 Value string
92 Comments []string
93}
94
95func newYamlNode(node *yaml.Node, offset int) *YamlNode {
96 return &YamlNode{
97 Position: NewFilePosition(nodeLines(node, offset)),
98 Value: node.Value,
99 Comments: mergeComments(node),
100 }
101}
102
103func newYamlNodeWithParent(parent, node *yaml.Node, offset int) *YamlNode {
104 return &YamlNode{
105 Position: NewFilePosition(nodeLines(node, offset)),
106 Value: node.Value,
107 Comments: mergeComments(node),
108 }
109}
110
111func newYamlKeyValue(key, val *yaml.Node, offset int) *YamlKeyValue {
112 return &YamlKeyValue{
113 Key: newYamlNode(key, offset),
114 Value: newYamlNodeWithParent(key, val, offset),
115 }
116}
117
118type YamlKeyValue struct {
119 Key *YamlNode
120 Value *YamlNode
121}
122
123func (ykv YamlKeyValue) Lines() (lines []int) {
124 lines = appendLine(lines, ykv.Key.Position.Lines...)
125 lines = appendLine(lines, ykv.Value.Position.Lines...)
126 return lines
127}
128
129type YamlMap struct {
130 Key *YamlNode
131 Items []*YamlKeyValue
132}
133
134func (ym YamlMap) Lines() (lines []int) {
135 lines = appendLine(lines, ym.Key.Position.Lines...)
136 for _, item := range ym.Items {
137 lines = appendLine(lines, item.Lines()...)
138 }
139 return lines
140}
141
142func (ym YamlMap) GetValue(key string) *YamlNode {
143 for _, child := range ym.Items {
144 if child.Key.Value == key {
145 return child.Value
146 }
147 }
148 return nil
149}
150
151func newYamlMap(key, value *yaml.Node, offset int) *YamlMap {
152 ym := YamlMap{
153 Key: newYamlNode(key, offset),
154 }
155
156 var ckey *yaml.Node
157 for _, child := range value.Content {
158 if ckey != nil {
159 kv := YamlKeyValue{
160 Key: newYamlNode(ckey, offset),
161 Value: newYamlNode(child, offset),
162 }
163 ym.Items = append(ym.Items, &kv)
164 ckey = nil
165 } else {
166 ckey = child
167 }
168 }
169
170 return &ym
171}
172
173type PromQLNode struct {
174 Expr string
175 Node promparser.Expr
176 Children []*PromQLNode
177}
178
179type PromQLError struct {
180 node *PromQLNode
181 Err error
182}
183
184func (pqle PromQLError) Error() string {
185 return pqle.Err.Error()
186}
187
188func (pqle *PromQLError) Unwrap() error {
189 return pqle.Err
190}
191
192func (pqle PromQLError) Node() *PromQLNode {
193 return pqle.node
194}
195
196type PromQLExpr struct {
197 Key *YamlNode
198 Value *YamlNode
199 SyntaxError error
200 Query *PromQLNode
201}
202
203func (pqle PromQLExpr) Lines() (lines []int) {
204 lines = appendLine(lines, pqle.Key.Position.Lines...)
205 lines = appendLine(lines, pqle.Value.Position.Lines...)
206 return lines
207}
208
209func newPromQLExpr(key, val *yaml.Node, offset int) *PromQLExpr {
210 expr := PromQLExpr{
211 Key: newYamlNode(key, offset),
212 Value: newYamlNodeWithParent(key, val, offset),
213 }
214
215 qlNode, err := DecodeExpr(val.Value)
216 if err != nil {
217 expr.SyntaxError = err
218 return &expr
219
220 }
221 expr.Query = qlNode
222 return &expr
223}
224
225type AlertingRule struct {
226 Alert YamlKeyValue
227 Expr PromQLExpr
228 For *YamlKeyValue
229 Labels *YamlMap
230 Annotations *YamlMap
231}
232
233func (ar AlertingRule) Lines() (lines []int) {
234 lines = appendLine(lines, ar.Alert.Lines()...)
235 lines = appendLine(lines, ar.Expr.Lines()...)
236 if ar.For != nil {
237 lines = appendLine(lines, ar.For.Lines()...)
238 }
239 if ar.Labels != nil {
240 lines = appendLine(lines, ar.Labels.Lines()...)
241 }
242 if ar.Annotations != nil {
243 lines = appendLine(lines, ar.Annotations.Lines()...)
244 }
245 slices.Sort(lines)
246 return lines
247}
248
249func (ar AlertingRule) Comments() (comments []string) {
250 comments = append(comments, ar.Alert.Key.Comments...)
251 comments = append(comments, ar.Alert.Value.Comments...)
252 comments = append(comments, ar.Expr.Key.Comments...)
253 comments = append(comments, ar.Expr.Value.Comments...)
254 if ar.For != nil {
255 comments = append(comments, ar.For.Key.Comments...)
256 comments = append(comments, ar.For.Value.Comments...)
257 }
258 if ar.Labels != nil {
259 comments = append(comments, ar.Labels.Key.Comments...)
260 for _, label := range ar.Labels.Items {
261 comments = append(comments, label.Key.Comments...)
262 comments = append(comments, label.Value.Comments...)
263 }
264 }
265 if ar.Annotations != nil {
266 comments = append(comments, ar.Annotations.Key.Comments...)
267 for _, annotation := range ar.Annotations.Items {
268 comments = append(comments, annotation.Key.Comments...)
269 comments = append(comments, annotation.Value.Comments...)
270 }
271 }
272 return comments
273}
274
275type RecordingRule struct {
276 Record YamlKeyValue
277 Expr PromQLExpr
278 Labels *YamlMap
279}
280
281func (rr RecordingRule) Lines() (lines []int) {
282 lines = appendLine(lines, rr.Record.Lines()...)
283 lines = appendLine(lines, rr.Expr.Lines()...)
284 if rr.Labels != nil {
285 lines = appendLine(lines, rr.Labels.Lines()...)
286 }
287 slices.Sort(lines)
288 return lines
289}
290
291func (rr RecordingRule) Comments() (comments []string) {
292 comments = append(comments, rr.Record.Key.Comments...)
293 comments = append(comments, rr.Record.Value.Comments...)
294 comments = append(comments, rr.Expr.Key.Comments...)
295 comments = append(comments, rr.Expr.Value.Comments...)
296 if rr.Labels != nil {
297 comments = append(comments, rr.Labels.Key.Comments...)
298 for _, label := range rr.Labels.Items {
299 comments = append(comments, label.Key.Comments...)
300 comments = append(comments, label.Value.Comments...)
301 }
302 }
303 return comments
304}
305
306type ParseError struct {
307 Fragment string
308 Err error
309 Line int
310}
311
312type Rule struct {
313 AlertingRule *AlertingRule
314 RecordingRule *RecordingRule
315 Error ParseError
316}
317
318func (r Rule) ToYAML() string {
319 if r.Error.Err != nil {
320 return fmt.Sprintf("line=%d fragment=%s err=%s", r.Error.Line, r.Error.Fragment, r.Error.Err)
321 }
322
323 if r.AlertingRule == nil && r.RecordingRule == nil {
324 return ""
325 }
326
327 var b strings.Builder
328 b.WriteString("- ")
329 if r.AlertingRule != nil {
330 b.WriteString(" ")
331 b.WriteString(r.AlertingRule.Alert.Key.Value)
332 b.WriteRune(':')
333 b.WriteString(r.AlertingRule.Alert.Value.Value)
334 b.WriteRune('\n')
335
336 b.WriteString(" ")
337 b.WriteString(r.AlertingRule.Expr.Key.Value)
338 b.WriteRune(':')
339 b.WriteString(r.AlertingRule.Expr.Value.Value)
340 b.WriteRune('\n')
341
342 if r.AlertingRule.For != nil {
343 b.WriteString(" ")
344 b.WriteString(r.AlertingRule.For.Key.Value)
345 b.WriteRune(':')
346 b.WriteString(r.AlertingRule.For.Value.Value)
347 b.WriteRune('\n')
348 }
349
350 if r.AlertingRule.Annotations != nil {
351 b.WriteString(" annotations:\n")
352 for _, a := range r.AlertingRule.Annotations.Items {
353 b.WriteString(" ")
354 b.WriteString(a.Key.Value)
355 b.WriteRune(':')
356 b.WriteString(a.Value.Value)
357 b.WriteRune('\n')
358 }
359 }
360
361 if r.AlertingRule.Labels != nil {
362 b.WriteString(" labels:\n")
363 for _, l := range r.AlertingRule.Labels.Items {
364 b.WriteString(" ")
365 b.WriteString(l.Key.Value)
366 b.WriteRune(':')
367 b.WriteString(l.Value.Value)
368 b.WriteRune('\n')
369 }
370 }
371
372 return b.String()
373 }
374
375 b.WriteString(r.RecordingRule.Record.Key.Value)
376 b.WriteRune(':')
377 b.WriteString(r.RecordingRule.Record.Value.Value)
378
379 b.WriteString(" ")
380 b.WriteString(r.RecordingRule.Expr.Key.Value)
381 b.WriteRune(':')
382 b.WriteString(r.RecordingRule.Expr.Value.Value)
383 b.WriteRune('\n')
384
385 if r.RecordingRule.Labels != nil {
386 b.WriteString(" labels:\n")
387 for _, l := range r.RecordingRule.Labels.Items {
388 b.WriteString(" ")
389 b.WriteString(l.Key.Value)
390 b.WriteRune(':')
391 b.WriteString(l.Value.Value)
392 b.WriteRune('\n')
393 }
394 }
395
396 return b.String()
397}
398
399func (r Rule) IsSame(nr Rule) bool {
400 if (r.AlertingRule != nil) != (nr.AlertingRule != nil) {
401 return false
402 }
403 if (r.RecordingRule != nil) != (nr.RecordingRule != nil) {
404 return false
405 }
406 if r.Error != nr.Error {
407 return false
408 }
409 if !slices.Equal(r.Lines(), nr.Lines()) {
410 return false
411 }
412 return true
413}
414
415func (r Rule) Name() string {
416 if r.RecordingRule != nil {
417 return r.RecordingRule.Record.Value.Value
418 }
419 if r.AlertingRule != nil {
420 return r.AlertingRule.Alert.Value.Value
421 }
422 return ""
423}
424
425func (r Rule) Expr() PromQLExpr {
426 if r.RecordingRule != nil {
427 return r.RecordingRule.Expr
428 }
429 return r.AlertingRule.Expr
430}
431
432func (r Rule) Lines() []int {
433 if r.RecordingRule != nil {
434 return r.RecordingRule.Lines()
435 }
436 if r.AlertingRule != nil {
437 return r.AlertingRule.Lines()
438 }
439 if r.Error.Err != nil {
440 return []int{r.Error.Line}
441 }
442 return nil
443}
444
445func (r Rule) LineRange() []int {
446 var lmin, lmax int
447 for i, line := range r.Lines() {
448 if i == 0 {
449 lmin = line
450 lmax = line
451 continue
452 }
453 if line < lmin {
454 lmin = line
455 }
456 if line > lmax {
457 lmax = line
458 }
459 }
460
461 lines := []int{}
462 for i := lmin; i <= lmax; i++ {
463 lines = append(lines, i)
464 }
465 return lines
466}
467
468func (r Rule) HasComment(comment string) bool {
469 var comments []string
470 if r.RecordingRule != nil {
471 comments = r.RecordingRule.Comments()
472 } else if r.AlertingRule != nil {
473 comments = r.AlertingRule.Comments()
474 }
475 for _, c := range comments {
476 if hasComment(c, comment) {
477 return true
478 }
479 }
480 return false
481}
482
483func (r Rule) GetComment(comment ...string) (s Comment, ok bool) {
484 var comments []string
485 if r.RecordingRule != nil {
486 comments = r.RecordingRule.Comments()
487 } else if r.AlertingRule != nil {
488 comments = r.AlertingRule.Comments()
489 }
490 for _, c := range comments {
491 var val Comment
492 if val, ok = GetLastComment(c, comment...); ok {
493 return val, ok
494 }
495 }
496 return s, ok
497}
498
499func (r Rule) GetComments(key string) (cs []Comment) {
500 var comments []string
501 if r.RecordingRule != nil {
502 comments = r.RecordingRule.Comments()
503 } else if r.AlertingRule != nil {
504 comments = r.AlertingRule.Comments()
505 }
506 for _, c := range comments {
507 sc := bufio.NewScanner(strings.NewReader(c))
508 for sc.Scan() {
509 if val, ok := GetLastComment(sc.Text(), key); ok {
510 cs = append(cs, val)
511 }
512 }
513 }
514 return cs
515}
516
517type RuleType string
518
519const (
520 AlertingRuleType RuleType = "alerting"
521 RecordingRuleType RuleType = "recording"
522 InvalidRuleType RuleType = "invalid"
523)
524
525func (r Rule) Type() RuleType {
526 if r.AlertingRule != nil {
527 return AlertingRuleType
528 }
529 if r.RecordingRule != nil {
530 return RecordingRuleType
531 }
532 return InvalidRuleType
533}
534
535type Result struct {
536 Path string
537 Error error
538 Content []byte
539 Rules []Rule
540}
541