cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.22.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

cmd/pint/scan.go

182lines · modecode

1package main
2
3import (
4 "context"
5 "regexp"
6 "strconv"
7 "sync"
8 "time"
9
10 "github.com/rs/zerolog/log"
11
12 "github.com/cloudflare/pint/internal/checks"
13 "github.com/cloudflare/pint/internal/config"
14 "github.com/cloudflare/pint/internal/discovery"
15 "github.com/cloudflare/pint/internal/output"
16 "github.com/cloudflare/pint/internal/reporter"
17)
18
19var (
20 yamlErrRe = regexp.MustCompile("^yaml: line (.+): (.+)")
21 yamlUnmarshalErrRe = regexp.MustCompile("^yaml: unmarshal errors:\n line (.+): (.+)")
22 rulefmtGroupRe = regexp.MustCompile("^([0-9]+):[0-9]+: group \".+\", rule [0-9]+, (.+)")
23 rulefmtGroupnameRe = regexp.MustCompile("^([0-9]+):[0-9]+: (groupname: .+)")
24)
25
26const yamlParseReporter = "yaml/parse"
27
28func tryDecodingYamlError(e string) (int, string) {
29 for _, re := range []*regexp.Regexp{yamlErrRe, yamlUnmarshalErrRe, rulefmtGroupRe, rulefmtGroupnameRe} {
30 parts := re.FindStringSubmatch(e)
31 if len(parts) > 2 {
32 line, err := strconv.Atoi(parts[1])
33 if err != nil {
34 return 1, e
35 }
36 return line, parts[2]
37 }
38 }
39 return 1, e
40}
41
42func checkRules(ctx context.Context, workers int, cfg config.Config, entries []discovery.Entry) (summary reporter.Summary) {
43 start := time.Now()
44 defer func() {
45 lastRunDuration.Set(time.Since(start).Seconds())
46 }()
47
48 jobs := make(chan scanJob, workers*5)
49 results := make(chan reporter.Report, workers*5)
50 wg := sync.WaitGroup{}
51
52 for w := 1; w <= workers; w++ {
53 wg.Add(1)
54 go func() {
55 defer wg.Done()
56 scanWorker(ctx, jobs, results)
57 }()
58 }
59
60 go func() {
61 defer close(results)
62 wg.Wait()
63 }()
64
65 go func() {
66 for _, entry := range entries {
67 if entry.PathError == nil && entry.Rule.Error.Err == nil {
68 if entry.Rule.RecordingRule != nil {
69 rulesParsedTotal.WithLabelValues(config.RecordingRuleType).Inc()
70 log.Debug().
71 Str("path", entry.Path).
72 Str("record", entry.Rule.RecordingRule.Record.Value.Value).
73 Str("lines", output.FormatLineRangeString(entry.Rule.Lines())).
74 Msg("Found recording rule")
75 }
76 if entry.Rule.AlertingRule != nil {
77 rulesParsedTotal.WithLabelValues(config.AlertingRuleType).Inc()
78 log.Debug().
79 Str("path", entry.Path).
80 Str("alert", entry.Rule.AlertingRule.Alert.Value.Value).
81 Str("lines", output.FormatLineRangeString(entry.Rule.Lines())).
82 Msg("Found alerting rule")
83 }
84
85 checkList := cfg.GetChecksForRule(ctx, entry.Path, entry.Rule)
86 for _, check := range checkList {
87 check := check
88 jobs <- scanJob{entry: entry, allEntries: entries, check: check}
89 }
90 } else {
91 if entry.Rule.Error.Err != nil {
92 log.Debug().
93 Str("path", entry.Path).
94 Str("lines", output.FormatLineRangeString(entry.Rule.Lines())).
95 Msg("Found invalid rule")
96 rulesParsedTotal.WithLabelValues(config.InvalidRuleType).Inc()
97 }
98
99 jobs <- scanJob{entry: entry, allEntries: entries, check: nil}
100 }
101 }
102 defer close(jobs)
103 }()
104
105 for result := range results {
106 summary.Reports = append(summary.Reports, result)
107 }
108
109 lastRunTime.SetToCurrentTime()
110
111 return
112}
113
114type scanJob struct {
115 allEntries []discovery.Entry
116 entry discovery.Entry
117 check checks.RuleChecker
118}
119
120func scanWorker(ctx context.Context, jobs <-chan scanJob, results chan<- reporter.Report) {
121 for job := range jobs {
122 job := job
123
124 select {
125 case <-ctx.Done():
126 return
127 default:
128 if job.entry.PathError != nil {
129 line, e := tryDecodingYamlError(job.entry.PathError.Error())
130 results <- reporter.Report{
131 Path: job.entry.Path,
132 ModifiedLines: job.entry.ModifiedLines,
133 Problem: checks.Problem{
134 Lines: []int{line},
135 Reporter: yamlParseReporter,
136 Text: e,
137 Severity: checks.Fatal,
138 },
139 Owner: job.entry.Owner,
140 }
141 } else if job.entry.Rule.Error.Err != nil {
142 results <- reporter.Report{
143 Path: job.entry.Path,
144 ModifiedLines: job.entry.ModifiedLines,
145 Rule: job.entry.Rule,
146 Problem: checks.Problem{
147 Fragment: job.entry.Rule.Error.Fragment,
148 Lines: []int{job.entry.Rule.Error.Line},
149 Reporter: yamlParseReporter,
150 Text: job.entry.Rule.Error.Err.Error(),
151 Severity: checks.Fatal,
152 },
153 Owner: job.entry.Owner,
154 }
155 } else {
156 start := time.Now()
157 problems := job.check.Check(ctx, job.entry.Rule, job.allEntries)
158 duration := time.Since(start)
159 checkDuration.WithLabelValues(job.check.Reporter()).Observe(duration.Seconds())
160 for _, problem := range problems {
161 results <- reporter.Report{
162 Path: job.entry.Path,
163 ModifiedLines: job.entry.ModifiedLines,
164 Rule: job.entry.Rule,
165 Problem: problem,
166 Owner: job.entry.Owner,
167 }
168 }
169 }
170 }
171 }
172}
173
174func submitReports(reps []reporter.Reporter, summary reporter.Summary) (err error) {
175 for _, rep := range reps {
176 err = rep.Submit(summary)
177 if err != nil {
178 return err
179 }
180 }
181 return nil
182}
183