cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.65.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/checks/alerts_count.go

134lines · modecode

1package checks
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7 "net/url"
8 "time"
9
10 "github.com/prometheus/common/model"
11
12 "github.com/cloudflare/pint/internal/discovery"
13 "github.com/cloudflare/pint/internal/output"
14 "github.com/cloudflare/pint/internal/parser"
15 "github.com/cloudflare/pint/internal/promapi"
16)
17
18const (
19 AlertsCheckName = "alerts/count"
20)
21
22func NewAlertsCheck(prom *promapi.FailoverGroup, lookBack, step, resolve time.Duration, minCount int, comment string, severity Severity) AlertsCheck {
23 return AlertsCheck{
24 prom: prom,
25 lookBack: lookBack,
26 step: step,
27 resolve: resolve,
28 minCount: minCount,
29 comment: comment,
30 severity: severity,
31 }
32}
33
34type AlertsCheck struct {
35 prom *promapi.FailoverGroup
36 comment string
37 lookBack time.Duration
38 step time.Duration
39 resolve time.Duration
40 minCount int
41 severity Severity
42}
43
44func (c AlertsCheck) Meta() CheckMeta {
45 return CheckMeta{
46 States: []discovery.ChangeType{
47 discovery.Noop,
48 discovery.Added,
49 discovery.Modified,
50 discovery.Moved,
51 },
52 IsOnline: true,
53 }
54}
55
56func (c AlertsCheck) String() string {
57 return fmt.Sprintf("%s(%s)", AlertsCheckName, c.prom.Name())
58}
59
60func (c AlertsCheck) Reporter() string {
61 return AlertsCheckName
62}
63
64func (c AlertsCheck) Check(ctx context.Context, _ discovery.Path, rule parser.Rule, _ []discovery.Entry) (problems []Problem) {
65 if rule.AlertingRule == nil {
66 return problems
67 }
68
69 if rule.AlertingRule.Expr.SyntaxError != nil {
70 return problems
71 }
72
73 params := promapi.NewRelativeRange(c.lookBack, c.step)
74
75 qr, err := c.prom.RangeQuery(ctx, rule.AlertingRule.Expr.Value.Value, params)
76 if err != nil {
77 text, severity := textAndSeverityFromError(err, c.Reporter(), c.prom.Name(), Bug)
78 problems = append(problems, Problem{
79 Lines: rule.AlertingRule.Expr.Value.Lines,
80 Reporter: c.Reporter(),
81 Text: text,
82 Severity: severity,
83 })
84 return problems
85 }
86
87 if len(qr.Series.Ranges) > 0 {
88 promUptime, err := c.prom.RangeQuery(ctx, fmt.Sprintf("count(%s)", c.prom.UptimeMetric()), params)
89 if err != nil {
90 slog.Warn("Cannot detect Prometheus uptime gaps", slog.Any("err", err), slog.String("name", c.prom.Name()))
91 } else {
92 // FIXME: gaps are not used
93 qr.Series.FindGaps(promUptime.Series, qr.Series.From, qr.Series.Until)
94 }
95 }
96
97 var forDur model.Duration
98 if rule.AlertingRule.For != nil {
99 forDur, _ = model.ParseDuration(rule.AlertingRule.For.Value)
100 }
101 var keepFiringForDur model.Duration
102 if rule.AlertingRule.KeepFiringFor != nil {
103 keepFiringForDur, _ = model.ParseDuration(rule.AlertingRule.KeepFiringFor.Value)
104 }
105
106 var alerts int
107 for _, r := range qr.Series.Ranges {
108 // If `keepFiringFor` is not defined its Duration will be 0
109 if r.End.Sub(r.Start) > (time.Duration(forDur) + time.Duration(keepFiringForDur)) {
110 alerts++
111 }
112 }
113
114 if alerts < c.minCount {
115 return problems
116 }
117
118 delta := qr.Series.Until.Sub(qr.Series.From).Round(time.Minute)
119 details := fmt.Sprintf(`To get a preview of the alerts that would fire please [click here](%s/graph?g0.expr=%s&g0.tab=0&g0.range_input=%s).`,
120 qr.URI, url.QueryEscape(rule.AlertingRule.Expr.Value.Value), output.HumanizeDuration(delta),
121 )
122 if c.comment != "" {
123 details = fmt.Sprintf("%s\n%s", details, maybeComment(c.comment))
124 }
125
126 problems = append(problems, Problem{
127 Lines: rule.AlertingRule.Expr.Value.Lines,
128 Reporter: c.Reporter(),
129 Text: fmt.Sprintf("%s would trigger %d alert(s) in the last %s.", promText(c.prom.Name(), qr.URI), alerts, output.HumanizeDuration(delta)),
130 Details: details,
131 Severity: c.severity,
132 })
133 return problems
134}
135