cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.43.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

532lines · 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 newYamlKeyValue(key, val *yaml.Node, offset int) *YamlKeyValue {
104 return &YamlKeyValue{
105 Key: newYamlNode(key, offset),
106 Value: newYamlNode(val, offset),
107 }
108}
109
110type YamlKeyValue struct {
111 Key *YamlNode
112 Value *YamlNode
113}
114
115func (ykv YamlKeyValue) Lines() (lines []int) {
116 lines = appendLine(lines, ykv.Key.Position.Lines...)
117 lines = appendLine(lines, ykv.Value.Position.Lines...)
118 return lines
119}
120
121type YamlMap struct {
122 Key *YamlNode
123 Items []*YamlKeyValue
124}
125
126func (ym YamlMap) Lines() (lines []int) {
127 lines = appendLine(lines, ym.Key.Position.Lines...)
128 for _, item := range ym.Items {
129 lines = appendLine(lines, item.Lines()...)
130 }
131 return lines
132}
133
134func (ym YamlMap) GetValue(key string) *YamlNode {
135 for _, child := range ym.Items {
136 if child.Key.Value == key {
137 return child.Value
138 }
139 }
140 return nil
141}
142
143func newYamlMap(key, value *yaml.Node, offset int) *YamlMap {
144 ym := YamlMap{
145 Key: newYamlNode(key, offset),
146 }
147
148 var ckey *yaml.Node
149 for _, child := range value.Content {
150 if ckey != nil {
151 kv := YamlKeyValue{
152 Key: newYamlNode(ckey, offset),
153 Value: newYamlNode(child, offset),
154 }
155 ym.Items = append(ym.Items, &kv)
156 ckey = nil
157 } else {
158 ckey = child
159 }
160 }
161
162 return &ym
163}
164
165type PromQLNode struct {
166 Expr string
167 Node promparser.Expr
168 Children []*PromQLNode
169}
170
171type PromQLError struct {
172 node *PromQLNode
173 Err error
174}
175
176func (pqle PromQLError) Error() string {
177 return pqle.Err.Error()
178}
179
180func (pqle *PromQLError) Unwrap() error {
181 return pqle.Err
182}
183
184func (pqle PromQLError) Node() *PromQLNode {
185 return pqle.node
186}
187
188type PromQLExpr struct {
189 Key *YamlNode
190 Value *YamlNode
191 SyntaxError error
192 Query *PromQLNode
193}
194
195func (pqle PromQLExpr) Lines() (lines []int) {
196 lines = appendLine(lines, pqle.Key.Position.Lines...)
197 lines = appendLine(lines, pqle.Value.Position.Lines...)
198 return lines
199}
200
201func newPromQLExpr(key, val *yaml.Node, offset int) *PromQLExpr {
202 expr := PromQLExpr{
203 Key: newYamlNode(key, offset),
204 Value: newYamlNode(val, offset),
205 }
206
207 qlNode, err := DecodeExpr(val.Value)
208 if err != nil {
209 expr.SyntaxError = err
210 return &expr
211
212 }
213 expr.Query = qlNode
214 return &expr
215}
216
217type AlertingRule struct {
218 Alert YamlKeyValue
219 Expr PromQLExpr
220 For *YamlKeyValue
221 Labels *YamlMap
222 Annotations *YamlMap
223}
224
225func (ar AlertingRule) Lines() (lines []int) {
226 lines = appendLine(lines, ar.Alert.Lines()...)
227 lines = appendLine(lines, ar.Expr.Lines()...)
228 if ar.For != nil {
229 lines = appendLine(lines, ar.For.Lines()...)
230 }
231 if ar.Labels != nil {
232 lines = appendLine(lines, ar.Labels.Lines()...)
233 }
234 if ar.Annotations != nil {
235 lines = appendLine(lines, ar.Annotations.Lines()...)
236 }
237 slices.Sort(lines)
238 return lines
239}
240
241func (ar AlertingRule) Comments() (comments []string) {
242 comments = append(comments, ar.Alert.Key.Comments...)
243 comments = append(comments, ar.Alert.Value.Comments...)
244 comments = append(comments, ar.Expr.Key.Comments...)
245 comments = append(comments, ar.Expr.Value.Comments...)
246 if ar.For != nil {
247 comments = append(comments, ar.For.Key.Comments...)
248 comments = append(comments, ar.For.Value.Comments...)
249 }
250 if ar.Labels != nil {
251 comments = append(comments, ar.Labels.Key.Comments...)
252 for _, label := range ar.Labels.Items {
253 comments = append(comments, label.Key.Comments...)
254 comments = append(comments, label.Value.Comments...)
255 }
256 }
257 if ar.Annotations != nil {
258 comments = append(comments, ar.Annotations.Key.Comments...)
259 for _, annotation := range ar.Annotations.Items {
260 comments = append(comments, annotation.Key.Comments...)
261 comments = append(comments, annotation.Value.Comments...)
262 }
263 }
264 return comments
265}
266
267type RecordingRule struct {
268 Record YamlKeyValue
269 Expr PromQLExpr
270 Labels *YamlMap
271}
272
273func (rr RecordingRule) Lines() (lines []int) {
274 lines = appendLine(lines, rr.Record.Lines()...)
275 lines = appendLine(lines, rr.Expr.Lines()...)
276 if rr.Labels != nil {
277 lines = appendLine(lines, rr.Labels.Lines()...)
278 }
279 slices.Sort(lines)
280 return lines
281}
282
283func (rr RecordingRule) Comments() (comments []string) {
284 comments = append(comments, rr.Record.Key.Comments...)
285 comments = append(comments, rr.Record.Value.Comments...)
286 comments = append(comments, rr.Expr.Key.Comments...)
287 comments = append(comments, rr.Expr.Value.Comments...)
288 if rr.Labels != nil {
289 comments = append(comments, rr.Labels.Key.Comments...)
290 for _, label := range rr.Labels.Items {
291 comments = append(comments, label.Key.Comments...)
292 comments = append(comments, label.Value.Comments...)
293 }
294 }
295 return comments
296}
297
298type ParseError struct {
299 Fragment string
300 Err error
301 Line int
302}
303
304type Rule struct {
305 AlertingRule *AlertingRule
306 RecordingRule *RecordingRule
307 Error ParseError
308}
309
310func (r Rule) ToYAML() string {
311 if r.Error.Err != nil {
312 return fmt.Sprintf("line=%d fragment=%s err=%s", r.Error.Line, r.Error.Fragment, r.Error.Err)
313 }
314
315 if r.AlertingRule == nil && r.RecordingRule == nil {
316 return ""
317 }
318
319 var b strings.Builder
320 b.WriteString("- ")
321 if r.AlertingRule != nil {
322 b.WriteString(" ")
323 b.WriteString(r.AlertingRule.Alert.Key.Value)
324 b.WriteRune(':')
325 b.WriteString(r.AlertingRule.Alert.Value.Value)
326 b.WriteRune('\n')
327
328 b.WriteString(" ")
329 b.WriteString(r.AlertingRule.Expr.Key.Value)
330 b.WriteRune(':')
331 b.WriteString(r.AlertingRule.Expr.Value.Value)
332 b.WriteRune('\n')
333
334 if r.AlertingRule.For != nil {
335 b.WriteString(" ")
336 b.WriteString(r.AlertingRule.For.Key.Value)
337 b.WriteRune(':')
338 b.WriteString(r.AlertingRule.For.Value.Value)
339 b.WriteRune('\n')
340 }
341
342 if r.AlertingRule.Annotations != nil {
343 b.WriteString(" annotations:\n")
344 for _, a := range r.AlertingRule.Annotations.Items {
345 b.WriteString(" ")
346 b.WriteString(a.Key.Value)
347 b.WriteRune(':')
348 b.WriteString(a.Value.Value)
349 b.WriteRune('\n')
350 }
351 }
352
353 if r.AlertingRule.Labels != nil {
354 b.WriteString(" labels:\n")
355 for _, l := range r.AlertingRule.Labels.Items {
356 b.WriteString(" ")
357 b.WriteString(l.Key.Value)
358 b.WriteRune(':')
359 b.WriteString(l.Value.Value)
360 b.WriteRune('\n')
361 }
362 }
363
364 return b.String()
365 }
366
367 b.WriteString(r.RecordingRule.Record.Key.Value)
368 b.WriteRune(':')
369 b.WriteString(r.RecordingRule.Record.Value.Value)
370
371 b.WriteString(" ")
372 b.WriteString(r.RecordingRule.Expr.Key.Value)
373 b.WriteRune(':')
374 b.WriteString(r.RecordingRule.Expr.Value.Value)
375 b.WriteRune('\n')
376
377 if r.RecordingRule.Labels != nil {
378 b.WriteString(" labels:\n")
379 for _, l := range r.RecordingRule.Labels.Items {
380 b.WriteString(" ")
381 b.WriteString(l.Key.Value)
382 b.WriteRune(':')
383 b.WriteString(l.Value.Value)
384 b.WriteRune('\n')
385 }
386 }
387
388 return b.String()
389}
390
391func (r Rule) IsSame(nr Rule) bool {
392 if (r.AlertingRule != nil) != (nr.AlertingRule != nil) {
393 return false
394 }
395 if (r.RecordingRule != nil) != (nr.RecordingRule != nil) {
396 return false
397 }
398 if r.Error != nr.Error {
399 return false
400 }
401 if !slices.Equal(r.Lines(), nr.Lines()) {
402 return false
403 }
404 return true
405}
406
407func (r Rule) Name() string {
408 if r.RecordingRule != nil {
409 return r.RecordingRule.Record.Value.Value
410 }
411 if r.AlertingRule != nil {
412 return r.AlertingRule.Alert.Value.Value
413 }
414 return ""
415}
416
417func (r Rule) Expr() PromQLExpr {
418 if r.RecordingRule != nil {
419 return r.RecordingRule.Expr
420 }
421 return r.AlertingRule.Expr
422}
423
424func (r Rule) Lines() []int {
425 if r.RecordingRule != nil {
426 return r.RecordingRule.Lines()
427 }
428 if r.AlertingRule != nil {
429 return r.AlertingRule.Lines()
430 }
431 if r.Error.Err != nil {
432 return []int{r.Error.Line}
433 }
434 return nil
435}
436
437func (r Rule) LineRange() []int {
438 var lmin, lmax int
439 for i, line := range r.Lines() {
440 if i == 0 {
441 lmin = line
442 lmax = line
443 continue
444 }
445 if line < lmin {
446 lmin = line
447 }
448 if line > lmax {
449 lmax = line
450 }
451 }
452
453 lines := []int{}
454 for i := lmin; i <= lmax; i++ {
455 lines = append(lines, i)
456 }
457 return lines
458}
459
460func (r Rule) HasComment(comment string) bool {
461 var comments []string
462 if r.RecordingRule != nil {
463 comments = r.RecordingRule.Comments()
464 } else if r.AlertingRule != nil {
465 comments = r.AlertingRule.Comments()
466 }
467 for _, c := range comments {
468 if hasComment(c, comment) {
469 return true
470 }
471 }
472 return false
473}
474
475func (r Rule) GetComment(comment ...string) (s Comment, ok bool) {
476 var comments []string
477 if r.RecordingRule != nil {
478 comments = r.RecordingRule.Comments()
479 } else if r.AlertingRule != nil {
480 comments = r.AlertingRule.Comments()
481 }
482 for _, c := range comments {
483 var val Comment
484 if val, ok = GetLastComment(c, comment...); ok {
485 return val, ok
486 }
487 }
488 return s, ok
489}
490
491func (r Rule) GetComments(key string) (cs []Comment) {
492 var comments []string
493 if r.RecordingRule != nil {
494 comments = r.RecordingRule.Comments()
495 } else if r.AlertingRule != nil {
496 comments = r.AlertingRule.Comments()
497 }
498 for _, c := range comments {
499 sc := bufio.NewScanner(strings.NewReader(c))
500 for sc.Scan() {
501 if val, ok := GetLastComment(sc.Text(), key); ok {
502 cs = append(cs, val)
503 }
504 }
505 }
506 return cs
507}
508
509type RuleType string
510
511const (
512 AlertingRuleType RuleType = "alerting"
513 RecordingRuleType RuleType = "recording"
514 InvalidRuleType RuleType = "invalid"
515)
516
517func (r Rule) Type() RuleType {
518 if r.AlertingRule != nil {
519 return AlertingRuleType
520 }
521 if r.RecordingRule != nil {
522 return RecordingRuleType
523 }
524 return InvalidRuleType
525}
526
527type Result struct {
528 Path string
529 Error error
530 Content []byte
531 Rules []Rule
532}
533