cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.29.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/discovery.go

143lines · modecode

1package discovery
2
3import (
4 "errors"
5 "os"
6 "regexp"
7 "strings"
8
9 "github.com/prometheus/prometheus/model/rulefmt"
10
11 "github.com/cloudflare/pint/internal/output"
12 "github.com/cloudflare/pint/internal/parser"
13
14 "github.com/rs/zerolog/log"
15)
16
17const (
18 FileOwnerComment = "file/owner"
19 RuleOwnerComment = "rule/owner"
20)
21
22var ignoredErrors = []string{
23 "one of 'record' or 'alert' must be set",
24 "field 'expr' must be set in rule",
25 "could not parse expression: ",
26 "cannot unmarshal !!seq into rulefmt.ruleGroups",
27 ": template: __",
28}
29
30func isStrictIgnored(err error) bool {
31 s := err.Error()
32
33 werr := &rulefmt.WrappedError{}
34 if errors.As(err, &werr) {
35 if uerr := werr.Unwrap(); uerr != nil {
36 s = uerr.Error()
37 }
38 }
39 for _, ign := range ignoredErrors {
40 if strings.Contains(s, ign) {
41 return true
42 }
43 }
44 return false
45}
46
47type RuleFinder interface {
48 Find() ([]Entry, error)
49}
50
51type Entry struct {
52 Path string
53 PathError error
54 ModifiedLines []int
55 Rule parser.Rule
56 Owner string
57}
58
59func readFile(path string, isStrict bool) (entries []Entry, err error) {
60 p := parser.NewParser()
61
62 f, err := os.Open(path)
63 if err != nil {
64 return nil, err
65 }
66
67 content, err := parser.ReadContent(f)
68 f.Close()
69 if err != nil {
70 return nil, err
71 }
72
73 contentLines := []int{}
74 for i := 1; i <= strings.Count(string(content), "\n"); i++ {
75 contentLines = append(contentLines, i)
76 }
77
78 fileOwner, _ := parser.GetComment(string(content), FileOwnerComment)
79
80 if isStrict {
81 if _, errs := rulefmt.Parse(content); len(errs) > 0 {
82 for _, err := range errs {
83 if isStrictIgnored(err) {
84 continue
85 }
86 log.Error().
87 Err(err).
88 Str("path", path).
89 Str("lines", output.FormatLineRangeString(contentLines)).
90 Msg("Failed to unmarshal file content")
91 entries = append(entries, Entry{
92 Path: path,
93 PathError: err,
94 Owner: fileOwner.Value,
95 ModifiedLines: contentLines,
96 })
97 }
98 if len(entries) > 0 {
99 return entries, nil
100 }
101 }
102 }
103
104 rules, err := p.Parse(content)
105 if err != nil {
106 log.Error().
107 Err(err).
108 Str("path", path).
109 Str("lines", output.FormatLineRangeString(contentLines)).
110 Msg("Failed to parse file content")
111 entries = append(entries, Entry{
112 Path: path,
113 PathError: err,
114 Owner: fileOwner.Value,
115 ModifiedLines: contentLines,
116 })
117 return entries, nil
118 }
119
120 for _, rule := range rules {
121 owner, ok := rule.GetComment(RuleOwnerComment)
122 if !ok {
123 owner = fileOwner
124 }
125 entries = append(entries, Entry{
126 Path: path,
127 Rule: rule,
128 Owner: owner.Value,
129 })
130 }
131
132 log.Debug().Str("path", path).Int("rules", len(entries)).Msg("File parsed")
133 return entries, nil
134}
135
136func matchesAny(re []*regexp.Regexp, s string) bool {
137 for _, r := range re {
138 if v := r.MatchString(s); v {
139 return true
140 }
141 }
142 return false
143}
144