|
| 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 | +} |
0 commit comments