cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.30.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/discovery.go

160lines · 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
30var ErrFileIsIgnored = errors.New("file was ignored")
31
32func isStrictIgnored(err error) bool {
33 s := err.Error()
34
35 werr := &rulefmt.WrappedError{}
36 if errors.As(err, &werr) {
37 if uerr := werr.Unwrap(); uerr != nil {
38 s = uerr.Error()
39 }
40 }
41 for _, ign := range ignoredErrors {
42 if strings.Contains(s, ign) {
43 return true
44 }
45 }
46 return false
47}
48
49type RuleFinder interface {
50 Find() ([]Entry, error)
51}
52
53type Entry struct {
54 ReportedPath string
55 SourcePath string
56 PathError error
57 ModifiedLines []int
58 Rule parser.Rule
59 Owner string
60}
61
62func readFile(path string, isStrict bool) (entries []Entry, err error) {
63 p := parser.NewParser()
64
65 f, err := os.Open(path)
66 if err != nil {
67 return nil, err
68 }
69
70 content, err := parser.ReadContent(f)
71 _ = f.Close()
72 if err != nil {
73 return nil, err
74 }
75
76 contentLines := []int{}
77 for i := 1; i <= strings.Count(string(content.Body), "\n"); i++ {
78 contentLines = append(contentLines, i)
79 }
80
81 fileOwner, _ := parser.GetComment(string(content.Body), FileOwnerComment)
82
83 if content.Ignored {
84 entries = append(entries, Entry{
85 ReportedPath: path,
86 SourcePath: path,
87 PathError: ErrFileIsIgnored,
88 Owner: fileOwner.Value,
89 ModifiedLines: contentLines,
90 })
91 return entries, nil
92 }
93
94 if isStrict {
95 if _, errs := rulefmt.Parse(content.Body); len(errs) > 0 {
96 for _, err := range errs {
97 if isStrictIgnored(err) {
98 continue
99 }
100 log.Error().
101 Err(err).
102 Str("path", path).
103 Str("lines", output.FormatLineRangeString(contentLines)).
104 Msg("Failed to unmarshal file content")
105 entries = append(entries, Entry{
106 ReportedPath: path,
107 SourcePath: path,
108 PathError: err,
109 Owner: fileOwner.Value,
110 ModifiedLines: contentLines,
111 })
112 }
113 if len(entries) > 0 {
114 return entries, nil
115 }
116 }
117 }
118
119 rules, err := p.Parse(content.Body)
120 if err != nil {
121 log.Error().
122 Err(err).
123 Str("path", path).
124 Str("lines", output.FormatLineRangeString(contentLines)).
125 Msg("Failed to parse file content")
126 entries = append(entries, Entry{
127 ReportedPath: path,
128 SourcePath: path,
129 PathError: err,
130 Owner: fileOwner.Value,
131 ModifiedLines: contentLines,
132 })
133 return entries, nil
134 }
135
136 for _, rule := range rules {
137 owner, ok := rule.GetComment(RuleOwnerComment)
138 if !ok {
139 owner = fileOwner
140 }
141 entries = append(entries, Entry{
142 ReportedPath: path,
143 SourcePath: path,
144 Rule: rule,
145 Owner: owner.Value,
146 })
147 }
148
149 log.Debug().Str("path", path).Int("rules", len(entries)).Msg("File parsed")
150 return entries, nil
151}
152
153func matchesAny(re []*regexp.Regexp, s string) bool {
154 for _, r := range re {
155 if v := r.MatchString(s); v {
156 return true
157 }
158 }
159 return false
160}
161