Skip to content

Commit d5853b3

Browse files
authored
Custom rulefmt package with remote-write configs (#82)
* feat: add remote write rules to rule fmt format Signed-off-by: Jacob Lisi <[email protected]> * check for RW configs when comparing rules and add tests Signed-off-by: Jacob Lisi <[email protected]>
1 parent 5de15ef commit d5853b3

File tree

9 files changed

+386
-72
lines changed

9 files changed

+386
-72
lines changed

pkg/client/rules.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import (
66
"net/url"
77

88
"github.com/pkg/errors"
9-
"github.com/prometheus/prometheus/pkg/rulefmt"
109
log "github.com/sirupsen/logrus"
1110
"gopkg.in/yaml.v3"
11+
12+
"github.com/grafana/cortex-tools/pkg/rules/rwrulefmt"
1213
)
1314

1415
// CreateRuleGroup creates a new rule group
15-
func (r *CortexClient) CreateRuleGroup(ctx context.Context, namespace string, rg rulefmt.RuleGroup) error {
16+
func (r *CortexClient) CreateRuleGroup(ctx context.Context, namespace string, rg rwrulefmt.RuleGroup) error {
1617
payload, err := yaml.Marshal(&rg)
1718
if err != nil {
1819
return err
@@ -39,7 +40,7 @@ func (r *CortexClient) DeleteRuleGroup(ctx context.Context, namespace, groupName
3940
}
4041

4142
// GetRuleGroup retrieves a rule group
42-
func (r *CortexClient) GetRuleGroup(ctx context.Context, namespace, groupName string) (*rulefmt.RuleGroup, error) {
43+
func (r *CortexClient) GetRuleGroup(ctx context.Context, namespace, groupName string) (*rwrulefmt.RuleGroup, error) {
4344
escapedNamespace := url.PathEscape(namespace)
4445
escapedGroupName := url.PathEscape(groupName)
4546
path := "/api/prom/rules/" + escapedNamespace + "/" + escapedGroupName
@@ -60,7 +61,7 @@ func (r *CortexClient) GetRuleGroup(ctx context.Context, namespace, groupName st
6061
return nil, err
6162
}
6263

63-
rg := rulefmt.RuleGroup{}
64+
rg := rwrulefmt.RuleGroup{}
6465
err = yaml.Unmarshal(body, &rg)
6566
if err != nil {
6667
log.WithFields(log.Fields{
@@ -74,7 +75,7 @@ func (r *CortexClient) GetRuleGroup(ctx context.Context, namespace, groupName st
7475
}
7576

7677
// ListRules retrieves a rule group
77-
func (r *CortexClient) ListRules(ctx context.Context, namespace string) (map[string][]rulefmt.RuleGroup, error) {
78+
func (r *CortexClient) ListRules(ctx context.Context, namespace string) (map[string][]rwrulefmt.RuleGroup, error) {
7879
path := "/api/prom/rules"
7980
if namespace != "" {
8081
path = path + "/" + namespace
@@ -92,7 +93,7 @@ func (r *CortexClient) ListRules(ctx context.Context, namespace string) (map[str
9293
return nil, err
9394
}
9495

95-
ruleSet := map[string][]rulefmt.RuleGroup{}
96+
ruleSet := map[string][]rwrulefmt.RuleGroup{}
9697
err = yaml.Unmarshal(body, &ruleSet)
9798
if err != nil {
9899
return nil, err

pkg/printer/printer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77

88
"github.com/alecthomas/chroma/quick"
99
"github.com/mitchellh/colorstring"
10-
"github.com/prometheus/prometheus/pkg/rulefmt"
1110
"gopkg.in/yaml.v3"
1211

1312
"github.com/grafana/cortex-tools/pkg/rules"
13+
"github.com/grafana/cortex-tools/pkg/rules/rwrulefmt"
1414
)
1515

1616
// Printer is used for printing formatted output from the cortextool
@@ -73,7 +73,7 @@ func (p *Printer) PrintAlertmanagerConfig(config string, templates map[string]st
7373
}
7474

7575
// PrintRuleGroups prints the current alertmanager config
76-
func (p *Printer) PrintRuleGroups(rules map[string][]rulefmt.RuleGroup) error {
76+
func (p *Printer) PrintRuleGroups(rules map[string][]rwrulefmt.RuleGroup) error {
7777
encodedRules, err := yaml.Marshal(&rules)
7878
if err != nil {
7979
return err
@@ -90,7 +90,7 @@ func (p *Printer) PrintRuleGroups(rules map[string][]rulefmt.RuleGroup) error {
9090
}
9191

9292
// PrintRuleGroup prints the current alertmanager config
93-
func (p *Printer) PrintRuleGroup(rule rulefmt.RuleGroup) error {
93+
func (p *Printer) PrintRuleGroup(rule rwrulefmt.RuleGroup) error {
9494
encodedRule, err := yaml.Marshal(&rule)
9595
if err != nil {
9696
return err

pkg/rules/compare.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ import (
99
"github.com/mitchellh/colorstring"
1010
"github.com/prometheus/prometheus/pkg/rulefmt"
1111
yaml "gopkg.in/yaml.v3"
12+
13+
"github.com/grafana/cortex-tools/pkg/rules/rwrulefmt"
1214
)
1315

1416
var (
15-
errNameDiff = errors.New("rule groups are named differently")
16-
errIntervalDiff = errors.New("rule groups have different intervals")
17-
errDiffRuleLen = errors.New("rule groups have a different number of rules")
17+
errNameDiff = errors.New("rule groups are named differently")
18+
errIntervalDiff = errors.New("rule groups have different intervals")
19+
errDiffRuleLen = errors.New("rule groups have a different number of rules")
20+
errDiffRWConfigs = errors.New("rule groups has different remote write configs")
1821
)
1922

2023
// NamespaceState is used to denote the difference between the staged namespace
@@ -38,8 +41,8 @@ type NamespaceChange struct {
3841
Namespace string
3942
State NamespaceState
4043
GroupsUpdated []UpdatedRuleGroup
41-
GroupsCreated []rulefmt.RuleGroup
42-
GroupsDeleted []rulefmt.RuleGroup
44+
GroupsCreated []rwrulefmt.RuleGroup
45+
GroupsDeleted []rwrulefmt.RuleGroup
4346
}
4447

4548
// SummarizeChanges returns the number of each type of change in a set of changes
@@ -61,12 +64,12 @@ func SummarizeChanges(changes []NamespaceChange) (created, updated, deleted int)
6164

6265
// UpdatedRuleGroup is used to store an change between a rule group
6366
type UpdatedRuleGroup struct {
64-
New rulefmt.RuleGroup
65-
Original rulefmt.RuleGroup
67+
New rwrulefmt.RuleGroup
68+
Original rwrulefmt.RuleGroup
6669
}
6770

6871
// CompareGroups differentiates between two rule groups
69-
func CompareGroups(groupOne, groupTwo rulefmt.RuleGroup) error {
72+
func CompareGroups(groupOne, groupTwo rwrulefmt.RuleGroup) error {
7073
if groupOne.Name != groupTwo.Name {
7174
return errNameDiff
7275
}
@@ -79,6 +82,16 @@ func CompareGroups(groupOne, groupTwo rulefmt.RuleGroup) error {
7982
return errDiffRuleLen
8083
}
8184

85+
if len(groupOne.RWConfigs) != len(groupTwo.RWConfigs) {
86+
return errDiffRWConfigs
87+
}
88+
89+
for i := range groupOne.RWConfigs {
90+
if groupOne.RWConfigs[i].URL != groupTwo.RWConfigs[i].URL {
91+
return errDiffRWConfigs
92+
}
93+
}
94+
8295
for i := range groupOne.Rules {
8396
eq := rulesEqual(&groupOne.Rules[i], &groupTwo.Rules[i])
8497
if !eq {
@@ -123,11 +136,11 @@ func CompareNamespaces(original, new RuleNamespace) NamespaceChange {
123136
Namespace: new.Namespace,
124137
State: Unchanged,
125138
GroupsUpdated: []UpdatedRuleGroup{},
126-
GroupsCreated: []rulefmt.RuleGroup{},
127-
GroupsDeleted: []rulefmt.RuleGroup{},
139+
GroupsCreated: []rwrulefmt.RuleGroup{},
140+
GroupsDeleted: []rwrulefmt.RuleGroup{},
128141
}
129142

130-
origMap := map[string]rulefmt.RuleGroup{}
143+
origMap := map[string]rwrulefmt.RuleGroup{}
131144
for _, g := range original.Groups {
132145
origMap[g.Name] = g
133146
}

pkg/rules/compare_test.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package rules
33
import (
44
"testing"
55

6+
"github.com/grafana/cortex-tools/pkg/rules/rwrulefmt"
67
"github.com/prometheus/prometheus/pkg/rulefmt"
78
yaml "gopkg.in/yaml.v3"
89
)
@@ -91,3 +92,197 @@ func Test_rulesEqual(t *testing.T) {
9192
})
9293
}
9394
}
95+
96+
func TestCompareGroups(t *testing.T) {
97+
tests := []struct {
98+
name string
99+
groupOne rwrulefmt.RuleGroup
100+
groupTwo rwrulefmt.RuleGroup
101+
expectedErr error
102+
}{
103+
{
104+
name: "identical configs",
105+
groupOne: rwrulefmt.RuleGroup{
106+
RuleGroup: rulefmt.RuleGroup{
107+
Name: "example_group",
108+
Rules: []rulefmt.RuleNode{
109+
{
110+
Record: yaml.Node{Value: "one"},
111+
Expr: yaml.Node{Value: "up"},
112+
Annotations: map[string]string{"a": "b", "c": "d"},
113+
Labels: nil,
114+
},
115+
},
116+
},
117+
},
118+
groupTwo: rwrulefmt.RuleGroup{
119+
RuleGroup: rulefmt.RuleGroup{
120+
Name: "example_group",
121+
Rules: []rulefmt.RuleNode{
122+
{
123+
Record: yaml.Node{Value: "one"},
124+
Expr: yaml.Node{Value: "up"},
125+
Annotations: map[string]string{"a": "b", "c": "d"},
126+
Labels: nil,
127+
},
128+
},
129+
},
130+
},
131+
expectedErr: nil,
132+
},
133+
{
134+
name: "different rule length",
135+
groupOne: rwrulefmt.RuleGroup{
136+
RuleGroup: rulefmt.RuleGroup{
137+
Name: "example_group",
138+
Rules: []rulefmt.RuleNode{
139+
{
140+
Record: yaml.Node{Value: "one"},
141+
Expr: yaml.Node{Value: "up"},
142+
Annotations: map[string]string{"a": "b", "c": "d"},
143+
Labels: nil,
144+
},
145+
},
146+
},
147+
},
148+
groupTwo: rwrulefmt.RuleGroup{
149+
RuleGroup: rulefmt.RuleGroup{
150+
Name: "example_group",
151+
Rules: []rulefmt.RuleNode{
152+
{
153+
Record: yaml.Node{Value: "one"},
154+
Expr: yaml.Node{Value: "up"},
155+
Annotations: map[string]string{"a": "b", "c": "d"},
156+
Labels: nil,
157+
},
158+
{
159+
Record: yaml.Node{Value: "one"},
160+
Expr: yaml.Node{Value: "up"},
161+
Annotations: map[string]string{"a": "b", "c": "d"},
162+
Labels: nil,
163+
},
164+
},
165+
},
166+
},
167+
expectedErr: errDiffRuleLen,
168+
},
169+
{
170+
name: "identical rw configs",
171+
groupOne: rwrulefmt.RuleGroup{
172+
RuleGroup: rulefmt.RuleGroup{
173+
Name: "example_group",
174+
Rules: []rulefmt.RuleNode{
175+
{
176+
Record: yaml.Node{Value: "one"},
177+
Expr: yaml.Node{Value: "up"},
178+
Annotations: map[string]string{"a": "b", "c": "d"},
179+
Labels: nil,
180+
},
181+
},
182+
},
183+
RWConfigs: []rwrulefmt.RemoteWriteConfig{
184+
{URL: "localhost"},
185+
},
186+
},
187+
groupTwo: rwrulefmt.RuleGroup{
188+
RuleGroup: rulefmt.RuleGroup{
189+
Name: "example_group",
190+
Rules: []rulefmt.RuleNode{
191+
{
192+
Record: yaml.Node{Value: "one"},
193+
Expr: yaml.Node{Value: "up"},
194+
Annotations: map[string]string{"a": "b", "c": "d"},
195+
Labels: nil,
196+
},
197+
},
198+
},
199+
RWConfigs: []rwrulefmt.RemoteWriteConfig{
200+
{URL: "localhost"},
201+
},
202+
},
203+
expectedErr: nil,
204+
},
205+
{
206+
name: "different rw config lengths",
207+
groupOne: rwrulefmt.RuleGroup{
208+
RuleGroup: rulefmt.RuleGroup{
209+
Name: "example_group",
210+
Rules: []rulefmt.RuleNode{
211+
{
212+
Record: yaml.Node{Value: "one"},
213+
Expr: yaml.Node{Value: "up"},
214+
Annotations: map[string]string{"a": "b", "c": "d"},
215+
Labels: nil,
216+
},
217+
},
218+
},
219+
RWConfigs: []rwrulefmt.RemoteWriteConfig{
220+
{URL: "localhost"},
221+
},
222+
},
223+
groupTwo: rwrulefmt.RuleGroup{
224+
RuleGroup: rulefmt.RuleGroup{
225+
Name: "example_group",
226+
Rules: []rulefmt.RuleNode{
227+
{
228+
Record: yaml.Node{Value: "one"},
229+
Expr: yaml.Node{Value: "up"},
230+
Annotations: map[string]string{"a": "b", "c": "d"},
231+
Labels: nil,
232+
},
233+
},
234+
},
235+
RWConfigs: []rwrulefmt.RemoteWriteConfig{
236+
{URL: "localhost"},
237+
{URL: "localhost"},
238+
},
239+
},
240+
expectedErr: errDiffRWConfigs,
241+
},
242+
{
243+
name: "different rw configs",
244+
groupOne: rwrulefmt.RuleGroup{
245+
RuleGroup: rulefmt.RuleGroup{
246+
Name: "example_group",
247+
Rules: []rulefmt.RuleNode{
248+
{
249+
Record: yaml.Node{Value: "one"},
250+
Expr: yaml.Node{Value: "up"},
251+
Annotations: map[string]string{"a": "b", "c": "d"},
252+
Labels: nil,
253+
},
254+
},
255+
},
256+
RWConfigs: []rwrulefmt.RemoteWriteConfig{
257+
{URL: "localhost"},
258+
},
259+
},
260+
groupTwo: rwrulefmt.RuleGroup{
261+
RuleGroup: rulefmt.RuleGroup{
262+
Name: "example_group",
263+
Rules: []rulefmt.RuleNode{
264+
{
265+
Record: yaml.Node{Value: "one"},
266+
Expr: yaml.Node{Value: "up"},
267+
Annotations: map[string]string{"a": "b", "c": "d"},
268+
Labels: nil,
269+
},
270+
},
271+
},
272+
RWConfigs: []rwrulefmt.RemoteWriteConfig{
273+
{URL: "localhost2"},
274+
},
275+
},
276+
expectedErr: errDiffRWConfigs,
277+
},
278+
}
279+
for _, tt := range tests {
280+
t.Run(tt.name, func(t *testing.T) {
281+
if err := CompareGroups(tt.groupOne, tt.groupTwo); err != nil {
282+
if err != tt.expectedErr {
283+
t.Errorf("CompareGroups() error = %v, wantErr %v", err, tt.expectedErr)
284+
}
285+
}
286+
})
287+
}
288+
}

pkg/rules/parser.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import (
99

1010
"github.com/grafana/loki/pkg/ruler/manager"
1111
log "github.com/sirupsen/logrus"
12-
1312
yaml "gopkg.in/yaml.v3"
13+
14+
"github.com/grafana/cortex-tools/pkg/rules/rwrulefmt"
1415
)
1516

1617
const (
@@ -46,8 +47,16 @@ func ParseFiles(backend string, files []string) (map[string]RuleNamespace, error
4647
return nil, errFileReadError
4748
}
4849

50+
rwRgs := []rwrulefmt.RuleGroup{}
51+
for _, rg := range rgs.Groups {
52+
rwRgs = append(rwRgs, rwrulefmt.RuleGroup{
53+
RuleGroup: rg,
54+
RWConfigs: nil,
55+
})
56+
}
57+
4958
ns = &RuleNamespace{
50-
Groups: rgs.Groups,
59+
Groups: rwRgs,
5160
}
5261

5362
default:

0 commit comments

Comments
 (0)