Skip to content

Commit 987ca67

Browse files
mattmendickpstibrany
authored andcommitted
Small fix for off-by-one rules limit enforcement
Signed-off-by: Matt Mendick <[email protected]>
1 parent c2cb4e1 commit 987ca67

File tree

4 files changed

+60
-9
lines changed

4 files changed

+60
-9
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* [BUGFIX] Query-frontend: Fix issue where cached entry size keeps increasing when making tiny query repeatedly. #3968
3737
* [BUGFIX] Compactor: `-compactor.blocks-retention-period` now supports weeks (`w`) and years (`y`). #4027
3838
* [BUGFIX] Querier: returning 422 (instead of 500) when query hits `max_chunks_per_query` limit with block storage, when the limit is hit in the store-gateway. #3937
39+
* [BUGFIX] Ruler: Rule group limit enforcement should now allow the same number of rules in a group as the limit. #3615
3940

4041
## Blocksconvert
4142

pkg/ruler/api.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ func (a *API) CreateRuleGroup(w http.ResponseWriter, req *http.Request) {
489489
return
490490
}
491491

492-
if err := a.ruler.AssertMaxRuleGroups(userID, len(rgs)); err != nil {
492+
if err := a.ruler.AssertMaxRuleGroups(userID, len(rgs)+1); err != nil {
493493
level.Error(logger).Log("msg", "limit validation failure", "err", err.Error(), "user", userID)
494494
http.Error(w, err.Error(), http.StatusBadRequest)
495495
return

pkg/ruler/api_test.go

+56-6
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ func TestRuler_DeleteNamespace(t *testing.T) {
300300
require.Equal(t, "{\"status\":\"error\",\"data\":null,\"errorType\":\"server_error\",\"error\":\"unable to delete rg\"}", w.Body.String())
301301
}
302302

303-
func TestRuler_Limits(t *testing.T) {
303+
func TestRuler_LimitsPerGroup(t *testing.T) {
304304
cfg, cleanup := defaultRulerConfig(newMockRuleStore(make(map[string]rulespb.RuleGroupList)))
305305
defer cleanup()
306306

@@ -338,24 +338,74 @@ rules:
338338
`,
339339
output: "per-user rules per rule group limit (limit: 1 actual: 2) exceeded\n",
340340
},
341+
}
342+
343+
for _, tt := range tc {
344+
t.Run(tt.name, func(t *testing.T) {
345+
router := mux.NewRouter()
346+
router.Path("/api/v1/rules/{namespace}").Methods("POST").HandlerFunc(a.CreateRuleGroup)
347+
// POST
348+
req := requestFor(t, http.MethodPost, "https://localhost:8080/api/v1/rules/namespace", strings.NewReader(tt.input), "user1")
349+
w := httptest.NewRecorder()
350+
351+
router.ServeHTTP(w, req)
352+
require.Equal(t, tt.status, w.Code)
353+
require.Equal(t, tt.output, w.Body.String())
354+
})
355+
}
356+
}
357+
358+
func TestRuler_RulerGroupLimits(t *testing.T) {
359+
cfg, cleanup := defaultRulerConfig(newMockRuleStore(make(map[string]rulespb.RuleGroupList)))
360+
defer cleanup()
361+
362+
r, rcleanup := newTestRuler(t, cfg)
363+
defer rcleanup()
364+
defer services.StopAndAwaitTerminated(context.Background(), r) //nolint:errcheck
365+
366+
r.limits = &ruleLimits{maxRuleGroups: 1, maxRulesPerRuleGroup: 1}
367+
368+
a := NewAPI(r, r.store, log.NewNopLogger())
369+
370+
tc := []struct {
371+
name string
372+
input string
373+
output string
374+
err error
375+
status int
376+
}{
377+
{
378+
name: "when pushing the first group within bounds of the limit",
379+
status: 202,
380+
input: `
381+
name: test_first_group_will_succeed
382+
interval: 15s
383+
rules:
384+
- record: up_rule
385+
expr: up{}
386+
`,
387+
output: "{\"status\":\"success\",\"data\":null,\"errorType\":\"\",\"error\":\"\"}",
388+
},
341389
{
342-
name: "when exceeding the rule group limit",
390+
name: "when exceeding the rule group limit after sending the first group",
343391
status: 400,
344392
input: `
345-
name: test
393+
name: test_second_group_will_fail
346394
interval: 15s
347395
rules:
348396
- record: up_rule
349397
expr: up{}
350398
`,
351-
output: "per-user rules per rule group limit (limit: 1 actual: 1) exceeded\n",
399+
output: "per-user rule groups limit (limit: 1 actual: 2) exceeded\n",
352400
},
353401
}
354402

403+
// define once so the requests build on each other so the number of rules can be tested
404+
router := mux.NewRouter()
405+
router.Path("/api/v1/rules/{namespace}").Methods("POST").HandlerFunc(a.CreateRuleGroup)
406+
355407
for _, tt := range tc {
356408
t.Run(tt.name, func(t *testing.T) {
357-
router := mux.NewRouter()
358-
router.Path("/api/v1/rules/{namespace}").Methods("POST").HandlerFunc(a.CreateRuleGroup)
359409
// POST
360410
req := requestFor(t, http.MethodPost, "https://localhost:8080/api/v1/rules/namespace", strings.NewReader(tt.input), "user1")
361411
w := httptest.NewRecorder()

pkg/ruler/ruler.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ func (r *Ruler) AssertMaxRuleGroups(userID string, rg int) error {
770770
return nil
771771
}
772772

773-
if rg < limit {
773+
if rg <= limit {
774774
return nil
775775
}
776776

@@ -786,7 +786,7 @@ func (r *Ruler) AssertMaxRulesPerRuleGroup(userID string, rules int) error {
786786
return nil
787787
}
788788

789-
if rules < limit {
789+
if rules <= limit {
790790
return nil
791791
}
792792
return fmt.Errorf(errMaxRulesPerRuleGroupPerUserLimitExceeded, limit, rules)

0 commit comments

Comments
 (0)