Skip to content

Commit 715fbba

Browse files
committed
feat: added thanos check rules command
Signed-off-by: Martin Chodur <[email protected]>
1 parent 8498d91 commit 715fbba

File tree

6 files changed

+176
-0
lines changed

6 files changed

+176
-0
lines changed

cmd/thanos/check.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
8+
"github.com/go-kit/kit/log"
9+
thanosrule "github.com/improbable-eng/thanos/pkg/rule"
10+
"github.com/oklog/run"
11+
"github.com/opentracing/opentracing-go"
12+
"github.com/prometheus/client_golang/prometheus"
13+
"github.com/prometheus/prometheus/pkg/rulefmt"
14+
"gopkg.in/alecthomas/kingpin.v2"
15+
"gopkg.in/yaml.v2"
16+
)
17+
18+
func registerChecks(m map[string]setupFunc, app *kingpin.Application, name string) {
19+
cmd := app.Command(name, "Linting tools for Thanos")
20+
registerRulesCheck(m, cmd, name)
21+
return
22+
}
23+
24+
func registerRulesCheck(m map[string]setupFunc, root *kingpin.CmdClause, name string) {
25+
checkRulesCmd := root.Command("rules", "Check if the rule files are valid or not.")
26+
ruleFiles := checkRulesCmd.Arg(
27+
"rule-files",
28+
"The rule files to check.",
29+
).Required().ExistingFiles()
30+
31+
m[name+" rules"] = func(g *run.Group, logger log.Logger, reg *prometheus.Registry, _ opentracing.Tracer, _ bool) error {
32+
// Dummy actor to immediately kill the group after the run function returns.
33+
g.Add(func() error { return nil }, func(error) {})
34+
return checkRulesFiles(ruleFiles)
35+
}
36+
}
37+
38+
func checkRulesFiles(files *[]string) error {
39+
var failed error
40+
41+
for _, f := range *files {
42+
n, errs := checkRules(f)
43+
if errs != nil {
44+
fmt.Fprintln(os.Stderr, " FAILED:")
45+
for _, e := range errs {
46+
fmt.Fprintln(os.Stderr, e.Error())
47+
}
48+
fmt.Println()
49+
failed = fmt.Errorf("Errors: %v\n\n", errs)
50+
continue
51+
}
52+
fmt.Printf(" SUCCESS: %d rules found\n\n", n)
53+
}
54+
if failed != nil {
55+
return failed
56+
}
57+
return nil
58+
}
59+
60+
func checkRules(filename string) (int, []error) {
61+
fmt.Println("Checking", filename)
62+
63+
b, err := ioutil.ReadFile(filename)
64+
if err != nil {
65+
return 0, []error{err}
66+
}
67+
68+
var rgs thanosrule.RuleGroups
69+
if err := yaml.Unmarshal(b, &rgs); err != nil {
70+
return 0, []error{err}
71+
}
72+
73+
// We need to convert Thanos rules to Prometheus rules so we can use their validation
74+
promRgs := thanosRuleGroupsToPromRuleGroups(rgs)
75+
if errs := promRgs.Validate(); errs != nil {
76+
return 0, errs
77+
}
78+
79+
numRules := 0
80+
for _, rg := range rgs.Groups {
81+
numRules += len(rg.Rules)
82+
}
83+
84+
return numRules, nil
85+
}
86+
87+
func thanosRuleGroupsToPromRuleGroups(ruleGroups thanosrule.RuleGroups) rulefmt.RuleGroups {
88+
promRuleGroups := rulefmt.RuleGroups{Groups: []rulefmt.RuleGroup{}}
89+
for _, g := range ruleGroups.Groups {
90+
group := rulefmt.RuleGroup{
91+
Name: g.Name,
92+
Interval: g.Interval,
93+
Rules: []rulefmt.Rule{},
94+
}
95+
for _, r := range g.Rules {
96+
group.Rules = append(
97+
group.Rules,
98+
rulefmt.Rule{
99+
Record: r.Record,
100+
Alert: r.Alert,
101+
Expr: r.Expr,
102+
For: r.For,
103+
Labels: r.Labels,
104+
Annotations: r.Annotations,
105+
},
106+
)
107+
}
108+
promRuleGroups.Groups = append(
109+
promRuleGroups.Groups,
110+
group,
111+
)
112+
}
113+
return promRuleGroups
114+
}

cmd/thanos/check_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/improbable-eng/thanos/pkg/testutil"
7+
)
8+
9+
func Test_checkRules(t *testing.T) {
10+
11+
validFiles := []string{
12+
"./testdata/rules-files/valid.yaml",
13+
}
14+
15+
invalidFiles := [][]string{
16+
[]string{"./testdata/rules-files/non-existing-file.yaml"},
17+
[]string{"./testdata/rules-files/invalid-yaml-format.yaml"},
18+
[]string{"./testdata/rules-files/invalid-rules-data.yaml"},
19+
}
20+
21+
testutil.Ok(t, checkRulesFiles(&validFiles))
22+
23+
for _, fn := range invalidFiles {
24+
testutil.NotOk(t, checkRulesFiles(&fn))
25+
}
26+
}

cmd/thanos/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func main() {
7979
registerBucket(cmds, app, "bucket")
8080
registerDownsample(cmds, app, "downsample")
8181
registerReceive(cmds, app, "receive")
82+
registerChecks(cmds, app, "check")
8283

8384
cmd, err := app.Parse(os.Args[1:])
8485
if err != nil {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
groups:
2+
- name: null
3+
partial_response_strategy: "warn"
4+
interval: 2m
5+
rules:
6+
- alert: TestAlert
7+
partial_response_strategy: "warn"
8+
expr: 1
9+
labels:
10+
key: value
11+
annotations:
12+
key: value
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
groups:
2+
- name: test
3+
invalid_yaml_reason
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
groups:
2+
- name: test-alert-group
3+
partial_response_strategy: "warn"
4+
interval: 2m
5+
rules:
6+
- alert: TestAlert
7+
partial_response_strategy: "warn"
8+
expr: 1
9+
labels:
10+
key: value
11+
annotations:
12+
key: value
13+
14+
- name: test-rule-group
15+
partial_response_strategy: "warn"
16+
interval: 2m
17+
rules:
18+
- record: test_metric
19+
expr: 1
20+
partial_response_strategy: "warn"

0 commit comments

Comments
 (0)