cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.49.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/checks/alerts_count.go

132lines · modecode

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