Alerting: rules delete API to check data source authorization (#46906)

* merge RuleSrv rule delete methods
* remove unused store methods
* implement delete by uid for fake store
* add scheduler mock
* implement tests for RouteDeleteAlertRules
This commit is contained in:
Yuriy Tseretyan
2022-03-25 12:39:24 -04:00
committed by GitHub
parent a06329d988
commit e20d157a9b
8 changed files with 508 additions and 111 deletions

View File

@@ -36,8 +36,6 @@ type UpsertRule struct {
// Store is the interface for persisting alert rules and instances
type RuleStore interface {
DeleteAlertRulesByUID(ctx context.Context, orgID int64, ruleUID ...string) error
DeleteNamespaceAlertRules(ctx context.Context, orgID int64, namespaceUID string) ([]string, error)
DeleteRuleGroupAlertRules(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) ([]string, error)
DeleteAlertInstancesByRuleUID(ctx context.Context, orgID int64, ruleUID string) error
GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) error
GetAlertRulesForScheduling(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error
@@ -88,76 +86,6 @@ func (st DBstore) DeleteAlertRulesByUID(ctx context.Context, orgID int64, ruleUI
})
}
// DeleteNamespaceAlertRules is a handler for deleting namespace alert rules. A list of deleted rule UIDs are returned.
func (st DBstore) DeleteNamespaceAlertRules(ctx context.Context, orgID int64, namespaceUID string) ([]string, error) {
ruleUIDs := []string{}
err := st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
if err := sess.SQL("SELECT uid FROM alert_rule WHERE org_id = ? and namespace_uid = ?", orgID, namespaceUID).Find(&ruleUIDs); err != nil {
return err
}
if _, err := sess.Exec("DELETE FROM alert_rule WHERE org_id = ? and namespace_uid = ?", orgID, namespaceUID); err != nil {
return err
}
if _, err := sess.Exec("DELETE FROM alert_rule WHERE org_id = ? and namespace_uid = ?", orgID, namespaceUID); err != nil {
return err
}
if _, err := sess.Exec("DELETE FROM alert_rule_version WHERE rule_org_id = ? and rule_namespace_uid = ?", orgID, namespaceUID); err != nil {
return err
}
if _, err := sess.Exec(`DELETE FROM alert_instance WHERE rule_org_id = ? AND rule_uid NOT IN (
SELECT uid FROM alert_rule where org_id = ?
)`, orgID, orgID); err != nil {
return err
}
return nil
})
return ruleUIDs, err
}
// DeleteRuleGroupAlertRules is a handler for deleting rule group alert rules. A list of deleted rule UIDs are returned.
func (st DBstore) DeleteRuleGroupAlertRules(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) ([]string, error) {
ruleUIDs := []string{}
err := st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
if err := sess.SQL("SELECT uid FROM alert_rule WHERE org_id = ? and namespace_uid = ? and rule_group = ?",
orgID, namespaceUID, ruleGroup).Find(&ruleUIDs); err != nil {
return err
}
exist, err := sess.Exist(&ngmodels.AlertRule{OrgID: orgID, NamespaceUID: namespaceUID, RuleGroup: ruleGroup})
if err != nil {
return err
}
if !exist {
return ngmodels.ErrRuleGroupNamespaceNotFound
}
if _, err := sess.Exec("DELETE FROM alert_rule WHERE org_id = ? and namespace_uid = ? and rule_group = ?", orgID, namespaceUID, ruleGroup); err != nil {
return err
}
if _, err := sess.Exec("DELETE FROM alert_rule_version WHERE rule_org_id = ? and rule_namespace_uid = ? and rule_group = ?", orgID, namespaceUID, ruleGroup); err != nil {
return err
}
if _, err := sess.Exec(`DELETE FROM alert_instance WHERE rule_org_id = ? AND rule_uid NOT IN (
SELECT uid FROM alert_rule where org_id = ?
)`, orgID, orgID); err != nil {
return err
}
return nil
})
return ruleUIDs, err
}
// DeleteAlertInstanceByRuleUID is a handler for deleting alert instances by alert rule UID when a rule has been updated
func (st DBstore) DeleteAlertInstancesByRuleUID(ctx context.Context, orgID int64, ruleUID string) error {
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {

View File

@@ -3,13 +3,16 @@ package store
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/http/httptest"
"sync"
"testing"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/util"
models2 "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ngalert/models"
@@ -27,6 +30,7 @@ func NewFakeRuleStore(t *testing.T) *FakeRuleStore {
Hook: func(interface{}) error {
return nil
},
Folders: map[int64][]*models2.Folder{},
}
}
@@ -38,6 +42,12 @@ type FakeRuleStore struct {
Rules map[int64][]*models.AlertRule
Hook func(cmd interface{}) error // use Hook if you need to intercept some query and return an error
RecordedOps []interface{}
Folders map[int64][]*models2.Folder
}
type GenericRecordedQuery struct {
Name string
Params []interface{}
}
// PutRule puts the rule in the Rules map. If there are existing rule in the same namespace, they will be overwritten
@@ -55,6 +65,23 @@ mainloop:
}
rgs = append(rgs, r)
f.Rules[r.OrgID] = rgs
var existing *models2.Folder
folders := f.Folders[r.OrgID]
for _, folder := range folders {
if folder.Uid == r.NamespaceUID {
existing = folder
break
}
}
if existing == nil {
folders = append(folders, &models2.Folder{
Id: rand.Int63(),
Uid: r.NamespaceUID,
Title: "TEST-FOLDER-" + util.GenerateShortUID(),
})
f.Folders[r.OrgID] = folders
}
}
}
@@ -74,15 +101,33 @@ func (f *FakeRuleStore) GetRecordedCommands(predicate func(cmd interface{}) (int
return result
}
func (f *FakeRuleStore) DeleteAlertRulesByUID(_ context.Context, _ int64, _ ...string) error {
func (f *FakeRuleStore) DeleteAlertRulesByUID(_ context.Context, orgID int64, UIDs ...string) error {
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
Name: "DeleteAlertRulesByUID",
Params: []interface{}{orgID, UIDs},
})
rules := f.Rules[orgID]
var result = make([]*models.AlertRule, 0, len(rules))
for _, rule := range rules {
add := true
for _, UID := range UIDs {
if rule.UID == UID {
add = false
break
}
}
if add {
result = append(result, rule)
}
}
f.Rules[orgID] = result
return nil
}
func (f *FakeRuleStore) DeleteNamespaceAlertRules(_ context.Context, _ int64, _ string) ([]string, error) {
return []string{}, nil
}
func (f *FakeRuleStore) DeleteRuleGroupAlertRules(_ context.Context, _ int64, _ string, _ string) ([]string, error) {
return []string{}, nil
}
func (f *FakeRuleStore) DeleteAlertInstancesByRuleUID(_ context.Context, _ int64, _ string) error {
return nil
}
@@ -179,8 +224,14 @@ func (f *FakeRuleStore) GetNamespaces(_ context.Context, orgID int64, _ *models2
}
return namespacesMap, nil
}
func (f *FakeRuleStore) GetNamespaceByTitle(_ context.Context, _ string, _ int64, _ *models2.SignedInUser, _ bool) (*models2.Folder, error) {
return nil, nil
func (f *FakeRuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *models2.SignedInUser, _ bool) (*models2.Folder, error) {
folders := f.Folders[orgID]
for _, folder := range folders {
if folder.Title == title {
return folder, nil
}
}
return nil, fmt.Errorf("not found")
}
func (f *FakeRuleStore) GetOrgRuleGroups(_ context.Context, q *models.ListOrgRuleGroupsQuery) error {
f.mtx.Lock()