cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.80.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

500lines · modecode

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