mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Don't validate rules on group update if they've only been reordered (#81841)
--------- Co-authored-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
This commit is contained in:
parent
16f5220adc
commit
b7bbc5058f
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -62,6 +63,9 @@ var (
|
|||||||
errProvisionedResource = errors.New("request affects resources created via provisioning API")
|
errProvisionedResource = errors.New("request affects resources created via provisioning API")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ignore fields that are not part of the rule definition
|
||||||
|
var ignoreFieldsForValidate = [...]string{"RuleGroupIndex"}
|
||||||
|
|
||||||
// RouteDeleteAlertRules deletes all alert rules the user is authorized to access in the given namespace
|
// RouteDeleteAlertRules deletes all alert rules the user is authorized to access in the given namespace
|
||||||
// or, if non-empty, a specific group of rules in the namespace.
|
// or, if non-empty, a specific group of rules in the namespace.
|
||||||
// Returns http.StatusForbidden if user does not have access to any of the rules that match the filter.
|
// Returns http.StatusForbidden if user does not have access to any of the rules that match the filter.
|
||||||
@ -558,6 +562,9 @@ func validateQueries(ctx context.Context, groupChanges *store.GroupDelta, valida
|
|||||||
}
|
}
|
||||||
if len(groupChanges.Update) > 0 {
|
if len(groupChanges.Update) > 0 {
|
||||||
for _, upd := range groupChanges.Update {
|
for _, upd := range groupChanges.Update {
|
||||||
|
if !shouldValidate(upd) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
err := validator.Validate(eval.NewContext(ctx, user), upd.New.GetEvalCondition())
|
err := validator.Validate(eval.NewContext(ctx, user), upd.New.GetEvalCondition())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w '%s' (UID: %s): %s", ngmodels.ErrAlertRuleFailedValidation, upd.New.Title, upd.New.UID, err.Error())
|
return fmt.Errorf("%w '%s' (UID: %s): %s", ngmodels.ErrAlertRuleFailedValidation, upd.New.Title, upd.New.UID, err.Error())
|
||||||
@ -567,6 +574,18 @@ func validateQueries(ctx context.Context, groupChanges *store.GroupDelta, valida
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shouldValidate returns true if the rule is not paused and there are changes in the rule that are not ignored
|
||||||
|
func shouldValidate(delta store.RuleDelta) bool {
|
||||||
|
for _, diff := range delta.Diff {
|
||||||
|
if !slices.Contains(ignoreFieldsForValidate[:], diff.Path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: consider also checking if rule will be paused after the update
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// getAuthorizedRuleByUid fetches all rules in group to which the specified rule belongs, and checks whether the user is authorized to access the group.
|
// getAuthorizedRuleByUid fetches all rules in group to which the specified rule belongs, and checks whether the user is authorized to access the group.
|
||||||
// A user is authorized to access a group of rules only when it has permission to query all data sources used by all rules in this group.
|
// A user is authorized to access a group of rules only when it has permission to query all data sources used by all rules in this group.
|
||||||
// Returns rule identified by provided UID or ErrAuthorization if user is not authorized to access the rule.
|
// Returns rule identified by provided UID or ErrAuthorization if user is not authorized to access the rule.
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
"github.com/grafana/grafana/pkg/util/cmputil"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -538,7 +540,24 @@ func TestValidateQueries(t *testing.T) {
|
|||||||
New: models.AlertRuleGen(func(rule *models.AlertRule) {
|
New: models.AlertRuleGen(func(rule *models.AlertRule) {
|
||||||
rule.Condition = "Update_New"
|
rule.Condition = "Update_New"
|
||||||
})(),
|
})(),
|
||||||
Diff: nil,
|
Diff: cmputil.DiffReport{
|
||||||
|
cmputil.Diff{
|
||||||
|
Path: "SomeField",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Existing: models.AlertRuleGen(func(rule *models.AlertRule) {
|
||||||
|
rule.Condition = "Update_Index_Existing"
|
||||||
|
})(),
|
||||||
|
New: models.AlertRuleGen(func(rule *models.AlertRule) {
|
||||||
|
rule.Condition = "Update_Index_New"
|
||||||
|
})(),
|
||||||
|
Diff: cmputil.DiffReport{
|
||||||
|
cmputil.Diff{
|
||||||
|
Path: "RuleGroupIndex",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Delete: []*models.AlertRule{
|
Delete: []*models.AlertRule{
|
||||||
@ -548,12 +567,13 @@ func TestValidateQueries(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("should validate New and Updated only", func(t *testing.T) {
|
t.Run("should not validate deleted rules or updated rules with ignored fields", func(t *testing.T) {
|
||||||
validator := &recordingConditionValidator{}
|
validator := &recordingConditionValidator{}
|
||||||
err := validateQueries(context.Background(), &delta, validator, nil)
|
err := validateQueries(context.Background(), &delta, validator, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
noValidate := []string{"Deleted", "Update_Index_New"}
|
||||||
for _, condition := range validator.recorded {
|
for _, condition := range validator.recorded {
|
||||||
if condition.Condition == "New" || condition.Condition == "Update_New" {
|
if !slices.Contains(noValidate, condition.Condition) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
assert.Failf(t, "validated unexpected condition", "condition '%s' was validated but should not", condition.Condition)
|
assert.Failf(t, "validated unexpected condition", "condition '%s' was validated but should not", condition.Condition)
|
||||||
|
Loading…
Reference in New Issue
Block a user