Skip to content

Commit bb8b04f

Browse files
authored
Add support for mixin alerting (#16)
1 parent a9694cf commit bb8b04f

File tree

7 files changed

+139
-75
lines changed

7 files changed

+139
-75
lines changed

generate/csv.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import "fmt"
44

55
// CSV finds all rules at the given path and its
66
// subdirectories and generates a CSV file of the found rules.
7-
func CSV(path string) (string, error) {
8-
ruleGroups, err := getRuleGroups(path)
7+
func CSV(path string, input string) (string, error) {
8+
ruleGroups, err := getRuleGroups(path, input)
99
if err != nil {
1010
return "", fmt.Errorf("get rule groups: %w", err)
1111
}

generate/csv_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func TestCSV(t *testing.T) {
1111
t.Fatal("read:", err)
1212
}
1313

14-
actual, err := Generate("../examples", ".csv")
14+
actual, err := Generate("../examples", ".csv", "kubernetes")
1515
if err != nil {
1616
t.Fatal("generate:", err)
1717
}

generate/generate.go

Lines changed: 11 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,100 +3,44 @@ package generate
33
import (
44
"errors"
55
"fmt"
6-
"io/ioutil"
76
"os"
87
"path/filepath"
98
"regexp"
109
"sort"
11-
12-
"github.com/ghodss/yaml"
1310
)
1411

15-
type typeMeta struct {
16-
Kind string `json:"kind"`
17-
}
18-
19-
type prometheusRule struct {
20-
Spec prometheusRuleSpec `json:"spec"`
21-
}
22-
23-
type prometheusRuleSpec struct {
24-
Groups []ruleGroup `json:"groups"`
25-
}
26-
27-
type ruleGroup struct {
28-
Name string `json:"name"`
29-
Rules []rule `json:"rules"`
30-
}
31-
32-
type rule struct {
33-
Alert string `json:"alert"`
34-
Labels map[string]string `json:"labels"`
35-
Annotations map[string]string `json:"annotations"`
36-
}
37-
3812
// Generate finds all rules at the given path and its
3913
// subdirectories and generates documentation with the
4014
// specified file extension, including the period.
41-
func Generate(path string, output string) (string, error) {
15+
func Generate(path string, output string, input string) (string, error) {
4216
switch output {
4317
case ".md":
44-
return Markdown(path)
18+
return Markdown(path, input)
4519
case ".csv":
46-
return CSV(path)
20+
return CSV(path, input)
4721
default:
4822
return "", errors.New("output format not supported")
4923
}
5024
}
5125

52-
func getRuleGroups(path string) ([]ruleGroup, error) {
26+
func getRuleGroups(path string, input string) ([]ruleGroup, error) {
5327
files, err := getYamlFiles(path)
5428
if err != nil {
5529
return nil, fmt.Errorf("get yaml files: %w", err)
5630
}
5731

5832
var alertGroups []ruleGroup
59-
for _, file := range files {
60-
fileBytes, err := ioutil.ReadFile(file)
33+
if "kubernetes" == input {
34+
alertGroups, err = getKubernetesRuleGroups(files)
6135
if err != nil {
62-
return nil, fmt.Errorf("open file: %w", err)
36+
return nil, err
6337
}
64-
65-
var typeMeta typeMeta
66-
if err := yaml.Unmarshal(fileBytes, &typeMeta); err != nil {
67-
continue
68-
}
69-
if typeMeta.Kind != "PrometheusRule" {
70-
continue
71-
}
72-
73-
var prometheusRule prometheusRule
74-
if err := yaml.Unmarshal(fileBytes, &prometheusRule); err != nil {
75-
continue
76-
}
77-
78-
for _, group := range prometheusRule.Spec.Groups {
79-
var alertRules []rule
80-
for _, rule := range group.Rules {
81-
if rule.Alert == "" {
82-
continue
83-
}
84-
85-
alertRules = append(alertRules, rule)
86-
}
87-
88-
if len(alertRules) == 0 {
89-
continue
90-
}
91-
92-
alertGroup := ruleGroup{
93-
Name: group.Name,
94-
Rules: alertRules,
95-
}
96-
alertGroups = append(alertGroups, alertGroup)
38+
} else {
39+
alertGroups, err = getMixinRuleGroups(files)
40+
if err != nil {
41+
return nil, err
9742
}
9843
}
99-
10044
sort.Slice(alertGroups, func(i int, j int) bool {
10145
return alertGroups[i].Name < alertGroups[j].Name
10246
})

generate/markdown.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
// Markdown finds all rules at the given path and its
99
// subdirectories and generates Markdown documentation
1010
// from the found rules.
11-
func Markdown(path string) (string, error) {
12-
ruleGroups, err := getRuleGroups(path)
11+
func Markdown(path string, input string) (string, error) {
12+
ruleGroups, err := getRuleGroups(path, input)
1313
if err != nil {
1414
return "", fmt.Errorf("get rule groups: %w", err)
1515
}

generate/markdown_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func TestMarkdown(t *testing.T) {
1111
t.Fatal("read:", err)
1212
}
1313

14-
actual, err := Generate("../examples", ".md")
14+
actual, err := Generate("../examples", ".md", "kubernetes")
1515
if err != nil {
1616
t.Fatal("generate:", err)
1717
}

generate/prometheus.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package generate
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
7+
"github.com/ghodss/yaml"
8+
)
9+
10+
type typeMeta struct {
11+
Kind string `json:"kind"`
12+
}
13+
14+
type prometheusRule struct {
15+
Spec prometheusRuleSpec `json:"spec"`
16+
}
17+
18+
type prometheusRuleSpec struct {
19+
Groups []ruleGroup `json:"groups"`
20+
}
21+
22+
type ruleGroup struct {
23+
Name string `json:"name"`
24+
Rules []rule `json:"rules"`
25+
}
26+
27+
type rule struct {
28+
Alert string `json:"alert"`
29+
Labels map[string]string `json:"labels"`
30+
Annotations map[string]string `json:"annotations"`
31+
}
32+
33+
func getMixinRuleGroups(files []string) ([]ruleGroup, error) {
34+
var alertGroups []ruleGroup
35+
36+
for _, file := range files {
37+
fileBytes, err := ioutil.ReadFile(file)
38+
if err != nil {
39+
return nil, fmt.Errorf("open file: %w", err)
40+
}
41+
42+
var groups prometheusRuleSpec
43+
if err := yaml.Unmarshal(fileBytes, &groups); err != nil {
44+
continue
45+
}
46+
47+
for _, group := range groups.Groups {
48+
alertGroup, err := extractGroupAlerts(group)
49+
if err != nil {
50+
return nil, err
51+
}
52+
alertGroups = append(alertGroups, *alertGroup)
53+
}
54+
55+
}
56+
57+
return alertGroups, nil
58+
}
59+
60+
func getKubernetesRuleGroups(files []string) ([]ruleGroup, error) {
61+
var alertGroups []ruleGroup
62+
for _, file := range files {
63+
fileBytes, err := ioutil.ReadFile(file)
64+
if err != nil {
65+
return nil, fmt.Errorf("open file: %w", err)
66+
}
67+
68+
var typeMeta typeMeta
69+
if err := yaml.Unmarshal(fileBytes, &typeMeta); err != nil {
70+
continue
71+
}
72+
if typeMeta.Kind != "PrometheusRule" {
73+
continue
74+
}
75+
76+
var prometheusRule prometheusRule
77+
if err := yaml.Unmarshal(fileBytes, &prometheusRule); err != nil {
78+
continue
79+
}
80+
81+
for _, group := range prometheusRule.Spec.Groups {
82+
alertGroup, err := extractGroupAlerts(group)
83+
if err != nil {
84+
return nil, err
85+
}
86+
if alertGroup != nil {
87+
alertGroups = append(alertGroups, *alertGroup)
88+
}
89+
}
90+
91+
}
92+
return alertGroups, nil
93+
}
94+
95+
func extractGroupAlerts(group ruleGroup) (*ruleGroup, error) {
96+
var alertRules []rule
97+
for _, rule := range group.Rules {
98+
if rule.Alert == "" {
99+
return nil, nil
100+
}
101+
102+
alertRules = append(alertRules, rule)
103+
}
104+
105+
if len(alertRules) == 0 {
106+
return nil, nil
107+
}
108+
109+
alertGroup := ruleGroup{
110+
Name: group.Name,
111+
Rules: alertRules,
112+
}
113+
return &alertGroup, nil
114+
}

internal/commands/generate.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func NewGenerateCommand() *cobra.Command {
2222
if err := viper.BindPFlag("out", cmd.Flags().Lookup("out")); err != nil {
2323
return fmt.Errorf("bind out flag: %w", err)
2424
}
25+
if err := viper.BindPFlag("in", cmd.Flags().Lookup("in")); err != nil {
26+
return fmt.Errorf("bind in flag: %w", err)
27+
}
28+
2529
return nil
2630
},
2731

@@ -40,7 +44,7 @@ func NewGenerateCommand() *cobra.Command {
4044
}
4145

4246
cmd.Flags().StringP("out", "o", "alerts.md", "File name or directory for the alert documentation")
43-
47+
cmd.Flags().StringP("in", "i", "kubernetes", "Alert style: Kubernetes or Mixin")
4448
return &cmd
4549
}
4650

@@ -50,7 +54,9 @@ func runGenerateCommand(path string) error {
5054
outputPath = filepath.Join(outputPath, "alerts.md")
5155
}
5256

53-
output, err := generate.Generate(path, filepath.Ext(outputPath))
57+
input := viper.GetString("in")
58+
59+
output, err := generate.Generate(path, filepath.Ext(outputPath), input)
5460
if err != nil {
5561
return fmt.Errorf("render: %w", err)
5662
}

0 commit comments

Comments
 (0)