cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.50.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

480lines · modecode

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