cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.81.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

512lines · modecode

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