cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.84.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

538lines · modecode

1package parser
2
3import (
4 "errors"
5 "fmt"
6 "slices"
7 "strconv"
8 "strings"
9 "sync"
10 "time"
11
12 "go.yaml.in/yaml/v3"
13
14 "github.com/prometheus/common/model"
15
16 "github.com/cloudflare/pint/internal/comments"
17 "github.com/cloudflare/pint/internal/diags"
18)
19
20func resolveNode(node *yaml.Node) *yaml.Node {
21 if node.Alias != nil {
22 return node.Alias
23 }
24 return node
25}
26
27func nodeValue(node *yaml.Node) string {
28 if node.Alias != nil {
29 return node.Alias.Value
30 }
31 return node.Value
32}
33
34type commentText struct {
35 text string
36 line int
37 column int
38}
39
40func commentColumn(contentLines []string, line int) int {
41 s := contentLines[line-1]
42 return len(s) - len(strings.TrimLeft(s, " \t"))
43}
44
45func mergeComments(node *yaml.Node, contentLines []string) (comments []commentText) {
46 if node.HeadComment != "" {
47 headLine := node.Line - strings.Count(node.HeadComment, "\n") - 1
48 comments = append(comments, commentText{
49 text: node.HeadComment,
50 line: headLine,
51 column: commentColumn(contentLines, headLine),
52 })
53 }
54 if node.LineComment != "" {
55 comments = append(comments, commentText{
56 text: node.LineComment,
57 line: node.Line,
58 column: 0,
59 })
60 }
61 if node.FootComment != "" {
62 footLine := node.Line + 1
63 comments = append(comments, commentText{
64 text: node.FootComment,
65 line: footLine,
66 column: commentColumn(contentLines, footLine),
67 })
68 }
69 for _, child := range node.Content {
70 comments = append(comments, mergeComments(child, contentLines)...)
71 }
72 return comments
73}
74
75type YamlNode struct {
76 Value string
77 Pos diags.PositionRanges
78}
79
80func (yn *YamlNode) IsIdentical(b *YamlNode) bool {
81 if (yn == nil) != (b == nil) {
82 return false
83 }
84 if yn == nil {
85 return true
86 }
87 if yn.Value != b.Value {
88 return false
89 }
90 return true
91}
92
93func newYamlNode(node *yaml.Node, offsetLine, offsetColumn int, contentLines []string, minColumn int) *YamlNode {
94 pos := diags.NewPositionRange(contentLines, node, minColumn)
95 pos.AddOffset(offsetLine, offsetColumn)
96 return &YamlNode{
97 Pos: pos,
98 Value: nodeValue(node),
99 }
100}
101
102type YamlDuration struct {
103 ParseError error
104 Raw string
105 Pos diags.PositionRanges
106 Value time.Duration
107}
108
109func (yd *YamlDuration) IsIdentical(b *YamlDuration) bool {
110 if (yd == nil) != (b == nil) {
111 return false
112 }
113 if yd == nil {
114 return true
115 }
116 return yd.Value == b.Value
117}
118
119type YamlInt struct {
120 ParseError error
121 Raw string
122 Pos diags.PositionRanges
123 Value int
124}
125
126func newYamlDuration(node *yaml.Node, offsetLine, offsetColumn int, contentLines []string, minColumn int) *YamlDuration {
127 pos := diags.NewPositionRange(contentLines, node, minColumn)
128 pos.AddOffset(offsetLine, offsetColumn)
129 yd := YamlDuration{
130 ParseError: nil,
131 Raw: nodeValue(node),
132 Pos: pos,
133 Value: 0,
134 }
135 dur, err := model.ParseDuration(nodeValue(node))
136 if err != nil {
137 yd.ParseError = err
138 } else {
139 yd.Value = time.Duration(dur)
140 }
141 return &yd
142}
143
144func newYamlInt(node *yaml.Node, offsetLine, offsetColumn int, contentLines []string, minColumn int) *YamlInt {
145 pos := diags.NewPositionRange(contentLines, node, minColumn)
146 pos.AddOffset(offsetLine, offsetColumn)
147 yi := YamlInt{
148 ParseError: nil,
149 Raw: nodeValue(node),
150 Pos: pos,
151 Value: 0,
152 }
153 val, err := strconv.Atoi(nodeValue(node))
154 if err != nil {
155 yi.ParseError = err
156 } else {
157 yi.Value = val
158 }
159 return &yi
160}
161
162type YamlKeyValue struct {
163 Key *YamlNode
164 Value *YamlNode
165}
166
167type YamlMap struct {
168 Key *YamlNode
169 Items []*YamlKeyValue
170}
171
172func (ym *YamlMap) IsIdentical(b *YamlMap) bool {
173 var aItems, bItems []*YamlKeyValue
174 if ym != nil {
175 aItems = ym.Items
176 }
177 if b != nil {
178 bItems = b.Items
179 }
180 if len(aItems) != len(bItems) {
181 return false
182 }
183
184 for _, ai := range aItems {
185 var found bool
186 for _, bi := range bItems {
187 if ai.Key.Value == bi.Key.Value {
188 if ai.Value.Value != bi.Value.Value {
189 return false
190 }
191 found = true
192 break
193 }
194 }
195 if !found {
196 return false
197 }
198 }
199
200 return true
201}
202
203func (ym YamlMap) GetValue(key string) *YamlNode {
204 for _, child := range ym.Items {
205 if child.Key.Value == key {
206 return child.Value
207 }
208 }
209 return nil
210}
211
212func (ym *YamlMap) setValue(item *YamlKeyValue) {
213 for i := range ym.Items {
214 if ym.Items[i].Key.Value == item.Key.Value {
215 ym.Items[i].Value = item.Value
216 return
217 }
218 }
219 ym.Items = append(ym.Items, item)
220}
221
222func (ym YamlMap) Lines() (lr diags.LineRange) {
223 lr = ym.Key.Pos.Lines()
224 for _, item := range ym.Items {
225 lr.First = min(lr.First, item.Key.Pos.Lines().First)
226 lr.Last = max(lr.Last, item.Value.Pos.Lines().Last)
227 }
228 return lr
229}
230
231func newYamlMap(key, value *yaml.Node, offsetLine, offsetColumn int, contentLines []string) *YamlMap {
232 ym := YamlMap{
233 Key: newYamlNode(key, offsetLine, offsetColumn, contentLines, 1),
234 Items: nil,
235 }
236
237 var ckey *yaml.Node
238 for _, child := range value.Content {
239 if ckey != nil {
240 kv := YamlKeyValue{
241 Key: newYamlNode(ckey, offsetLine, offsetColumn, contentLines, key.Column+2),
242 Value: newYamlNode(child, offsetLine, offsetColumn, contentLines, ckey.Column+2),
243 }
244 ym.Items = append(ym.Items, &kv)
245 ckey = nil
246 } else {
247 ckey = child
248 }
249 }
250
251 return &ym
252}
253
254func (pqle *PromQLExpr) IsIdentical(b PromQLExpr) bool {
255 return pqle.Value.Value == b.Value.Value
256}
257
258func newPromQLExpr(node *yaml.Node, offsetLine, offsetColumn int, contentLines []string, minColumn int) *PromQLExpr {
259 return &PromQLExpr{
260 Value: newYamlNode(node, offsetLine, offsetColumn, contentLines, minColumn),
261 syntaxError: nil,
262 query: nil,
263 mu: &sync.Mutex{},
264 source: nil,
265 hasSource: false,
266 }
267}
268
269type AlertingRule struct {
270 For *YamlDuration
271 KeepFiringFor *YamlDuration
272 Labels *YamlMap
273 Annotations *YamlMap
274 Alert YamlNode
275 Expr PromQLExpr
276}
277
278func (ar *AlertingRule) IsIdentical(b *AlertingRule) bool {
279 if (ar == nil) != (b == nil) {
280 return false
281 }
282 if ar == nil {
283 return true
284 }
285 if !ar.Alert.IsIdentical(&b.Alert) {
286 return false
287 }
288 if !ar.Expr.IsIdentical(b.Expr) {
289 return false
290 }
291 if !ar.For.IsIdentical(b.For) {
292 return false
293 }
294 if !ar.KeepFiringFor.IsIdentical(b.KeepFiringFor) {
295 return false
296 }
297 if !ar.Labels.IsIdentical(b.Labels) {
298 return false
299 }
300 if !ar.Annotations.IsIdentical(b.Annotations) {
301 return false
302 }
303 return true
304}
305
306type RecordingRule struct {
307 Labels *YamlMap
308 Record YamlNode
309 Expr PromQLExpr
310}
311
312func (rr *RecordingRule) IsIdentical(b *RecordingRule) bool {
313 if (rr == nil) != (b == nil) {
314 return false
315 }
316 if rr == nil {
317 return true
318 }
319 if !rr.Record.IsIdentical(&b.Record) {
320 return false
321 }
322 if !rr.Expr.IsIdentical(b.Expr) {
323 return false
324 }
325 if !rr.Labels.IsIdentical(b.Labels) {
326 return false
327 }
328 return true
329}
330
331// Use insread of StrictError.
332type ParseError struct {
333 Err error
334 Details string
335 Diagnostics []diags.Diagnostic
336 Line int
337}
338
339func (pe ParseError) Error() string {
340 return fmt.Sprintf("error at line %d: %s", pe.Line, pe.Err)
341}
342
343type File struct {
344 Diagnostics []diags.Diagnostic
345 Comments []comments.Comment
346 Groups []Group
347 Error ParseError
348 TotalLines int
349 IsRelaxed bool
350 IsIgnored bool
351}
352
353type Group struct {
354 Labels *YamlMap
355 Interval *YamlDuration
356 QueryOffset *YamlDuration
357 Limit *YamlInt
358 Name YamlNode
359 Rules []Rule
360 Error ParseError
361}
362
363type Rule struct {
364 AlertingRule *AlertingRule
365 RecordingRule *RecordingRule
366 Comments []comments.Comment
367 Error ParseError
368 Lines diags.LineRange
369}
370
371func (r Rule) IsIdentical(b Rule) bool {
372 if r.Type() != b.Type() {
373 return false
374 }
375 if !r.AlertingRule.IsIdentical(b.AlertingRule) {
376 return false
377 }
378 if !r.RecordingRule.IsIdentical(b.RecordingRule) {
379 return false
380 }
381
382 // Early exit if comment counts differ.
383 if len(r.Comments) != len(b.Comments) {
384 return false
385 }
386
387 // Fast path: if both have no comments, they're equal.
388 if len(r.Comments) == 0 {
389 return true
390 }
391
392 ac := make([]string, 0, len(r.Comments))
393 for _, c := range r.Comments {
394 ac = append(ac, c.Value.String())
395 }
396 slices.Sort(ac)
397
398 bc := make([]string, 0, len(r.Comments))
399 for _, c := range b.Comments {
400 bc = append(bc, c.Value.String())
401 }
402 slices.Sort(bc)
403
404 return slices.Equal(ac, bc)
405}
406
407func (r Rule) IsSame(nr Rule) bool {
408 if (r.AlertingRule != nil) != (nr.AlertingRule != nil) {
409 return false
410 }
411 if (r.RecordingRule != nil) != (nr.RecordingRule != nil) {
412 return false
413 }
414 if r.Error.Line != nr.Error.Line {
415 return false
416 }
417 if !errors.Is(r.Error.Err, nr.Error.Err) {
418 return false
419 }
420 if r.Lines.First != nr.Lines.First {
421 return false
422 }
423 if r.Lines.Last != nr.Lines.Last {
424 return false
425 }
426 return true
427}
428
429func (r Rule) Name() string {
430 if r.RecordingRule != nil {
431 return r.RecordingRule.Record.Value
432 }
433 if r.AlertingRule != nil {
434 return r.AlertingRule.Alert.Value
435 }
436 return ""
437}
438
439func (r Rule) NameNode() YamlNode {
440 if r.RecordingRule != nil {
441 return r.RecordingRule.Record
442 }
443 return r.AlertingRule.Alert
444}
445
446func (r Rule) Expr() *PromQLExpr {
447 if r.RecordingRule != nil {
448 return &r.RecordingRule.Expr
449 }
450 return &r.AlertingRule.Expr
451}
452
453func (r Rule) LastKey() (node *YamlNode) {
454 if r.RecordingRule != nil {
455 node = &r.RecordingRule.Record
456 if r.RecordingRule.Expr.Value.Pos.Lines().Last > node.Pos.Lines().Last {
457 node = r.RecordingRule.Expr.Value
458 }
459 if r.RecordingRule.Labels != nil {
460 for _, lab := range r.RecordingRule.Labels.Items {
461 if lab.Key.Pos.Lines().Last > node.Pos.Lines().Last {
462 node = lab.Key
463 }
464 }
465 }
466 }
467 if r.AlertingRule != nil {
468 node = &r.AlertingRule.Alert
469 if r.AlertingRule.Expr.Value.Pos.Lines().Last > node.Pos.Lines().Last {
470 node = r.AlertingRule.Expr.Value
471 }
472 if r.AlertingRule.For != nil && r.AlertingRule.For.Pos.Lines().Last > node.Pos.Lines().Last {
473 node = &YamlNode{
474 Pos: r.AlertingRule.For.Pos,
475 Value: r.AlertingRule.For.Raw,
476 }
477 }
478 if r.AlertingRule.KeepFiringFor != nil && r.AlertingRule.KeepFiringFor.Pos.Lines().Last > node.Pos.Lines().Last {
479 node = &YamlNode{
480 Pos: r.AlertingRule.KeepFiringFor.Pos,
481 Value: r.AlertingRule.KeepFiringFor.Raw,
482 }
483 }
484 if r.AlertingRule.Labels != nil {
485 for _, lab := range r.AlertingRule.Labels.Items {
486 if lab.Key.Pos.Lines().Last > node.Pos.Lines().Last {
487 node = lab.Key
488 }
489 }
490 }
491 if r.AlertingRule.Annotations != nil {
492 for _, ann := range r.AlertingRule.Annotations.Items {
493 if ann.Key.Pos.Lines().Last > node.Pos.Lines().Last {
494 node = ann.Key
495 }
496 }
497 }
498 }
499 return node
500}
501
502type RuleType string
503
504const (
505 AlertingRuleType RuleType = "alerting"
506 RecordingRuleType RuleType = "recording"
507 InvalidRuleType RuleType = "invalid"
508)
509
510func (r Rule) Type() RuleType {
511 if r.AlertingRule != nil {
512 return AlertingRuleType
513 }
514 if r.RecordingRule != nil {
515 return RecordingRuleType
516 }
517 return InvalidRuleType
518}
519
520func MergeMaps(a, b *YamlMap) *YamlMap {
521 if a == nil {
522 return b
523 }
524 if b == nil {
525 return a
526 }
527
528 dst := &YamlMap{
529 Key: a.Key,
530 Items: slices.Clone(a.Items),
531 }
532
533 for _, item := range b.Items {
534 dst.setValue(item)
535 }
536
537 return dst
538}
539