Alerting: Export and provisioning rules into subfolders (#77450)
* Folders: Optionally include fullpath in service responses * Alerting: Export folder fullpath instead of title * Escape separator in folder title * Add support for provisiong alret rules into subfolders * Use FolderService for creating folders during provisioning * Export WithFullpath() folder service function --------- Co-authored-by: Tania B <yalyna.ts@gmail.com> Co-authored-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
This commit is contained in:
co-authored by
Tania B
Yuri Tseretyan
parent
e1aedb65b3
commit
17ca61d7f8
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/hcl"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
alerting_models "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
@@ -28,6 +29,7 @@ type ProvisioningSrv struct {
|
||||
templates TemplateService
|
||||
muteTimings MuteTimingService
|
||||
alertRules AlertRuleService
|
||||
folderSvc folder.Service
|
||||
}
|
||||
|
||||
type ContactPointService interface {
|
||||
@@ -66,9 +68,9 @@ type AlertRuleService interface {
|
||||
GetRuleGroup(ctx context.Context, user identity.Requester, folder, group string) (alerting_models.AlertRuleGroup, error)
|
||||
ReplaceRuleGroup(ctx context.Context, user identity.Requester, group alerting_models.AlertRuleGroup, provenance alerting_models.Provenance) error
|
||||
DeleteRuleGroup(ctx context.Context, user identity.Requester, folder, group string, provenance alerting_models.Provenance) error
|
||||
GetAlertRuleWithFolderTitle(ctx context.Context, user identity.Requester, ruleUID string) (provisioning.AlertRuleWithFolderTitle, error)
|
||||
GetAlertRuleGroupWithFolderTitle(ctx context.Context, user identity.Requester, folder, group string) (alerting_models.AlertRuleGroupWithFolderTitle, error)
|
||||
GetAlertGroupsWithFolderTitle(ctx context.Context, user identity.Requester, folderUIDs []string) ([]alerting_models.AlertRuleGroupWithFolderTitle, error)
|
||||
GetAlertRuleWithFolderFullpath(ctx context.Context, u identity.Requester, ruleUID string) (provisioning.AlertRuleWithFolderFullpath, error)
|
||||
GetAlertRuleGroupWithFolderFullpath(ctx context.Context, u identity.Requester, folder, group string) (alerting_models.AlertRuleGroupWithFolderFullpath, error)
|
||||
GetAlertGroupsWithFolderFullpath(ctx context.Context, u identity.Requester, folderUIDs []string) ([]alerting_models.AlertRuleGroupWithFolderFullpath, error)
|
||||
}
|
||||
|
||||
func (srv *ProvisioningSrv) RouteGetPolicyTree(c *contextmodel.ReqContext) response.Response {
|
||||
@@ -422,15 +424,15 @@ func (srv *ProvisioningSrv) RouteGetAlertRulesExport(c *contextmodel.ReqContext)
|
||||
return srv.RouteGetAlertRuleGroupExport(c, folderUIDs[0], group)
|
||||
}
|
||||
|
||||
groupsWithTitle, err := srv.alertRules.GetAlertGroupsWithFolderTitle(c.Req.Context(), c.SignedInUser, folderUIDs)
|
||||
groupsWithFullpath, err := srv.alertRules.GetAlertGroupsWithFolderFullpath(c.Req.Context(), c.SignedInUser, folderUIDs)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to get alert rules", err)
|
||||
}
|
||||
if len(groupsWithTitle) == 0 {
|
||||
if len(groupsWithFullpath) == 0 {
|
||||
return response.Empty(http.StatusNotFound)
|
||||
}
|
||||
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle(groupsWithTitle)
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderFullpath(groupsWithFullpath)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to create alerting file export", err)
|
||||
}
|
||||
@@ -440,12 +442,12 @@ func (srv *ProvisioningSrv) RouteGetAlertRulesExport(c *contextmodel.ReqContext)
|
||||
|
||||
// RouteGetAlertRuleGroupExport retrieves the given alert rule group in a format compatible with file provisioning.
|
||||
func (srv *ProvisioningSrv) RouteGetAlertRuleGroupExport(c *contextmodel.ReqContext, folder string, group string) response.Response {
|
||||
g, err := srv.alertRules.GetAlertRuleGroupWithFolderTitle(c.Req.Context(), c.SignedInUser, folder, group)
|
||||
g, err := srv.alertRules.GetAlertRuleGroupWithFolderFullpath(c.Req.Context(), c.SignedInUser, folder, group)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to get alert rule group", err)
|
||||
}
|
||||
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle([]alerting_models.AlertRuleGroupWithFolderTitle{g})
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderFullpath([]alerting_models.AlertRuleGroupWithFolderFullpath{g})
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to create alerting file export", err)
|
||||
}
|
||||
@@ -455,7 +457,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleGroupExport(c *contextmodel.ReqCont
|
||||
|
||||
// RouteGetAlertRuleExport retrieves the given alert rule in a format compatible with file provisioning.
|
||||
func (srv *ProvisioningSrv) RouteGetAlertRuleExport(c *contextmodel.ReqContext, UID string) response.Response {
|
||||
rule, err := srv.alertRules.GetAlertRuleWithFolderTitle(c.Req.Context(), c.SignedInUser, UID)
|
||||
rule, err := srv.alertRules.GetAlertRuleWithFolderFullpath(c.Req.Context(), c.SignedInUser, UID)
|
||||
if err != nil {
|
||||
if errors.Is(err, alerting_models.ErrAlertRuleNotFound) {
|
||||
return ErrResp(http.StatusNotFound, err, "")
|
||||
@@ -463,8 +465,8 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleExport(c *contextmodel.ReqContext,
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to get alert rules", err)
|
||||
}
|
||||
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle([]alerting_models.AlertRuleGroupWithFolderTitle{
|
||||
alerting_models.NewAlertRuleGroupWithFolderTitleFromRulesGroup(rule.AlertRule.GetGroupKey(), alerting_models.RulesGroup{&rule.AlertRule}, rule.FolderTitle),
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderFullpath([]alerting_models.AlertRuleGroupWithFolderFullpath{
|
||||
alerting_models.NewAlertRuleGroupWithFolderFullpathFromRulesGroup(rule.AlertRule.GetGroupKey(), alerting_models.RulesGroup{&rule.AlertRule}, rule.FolderFullpath),
|
||||
})
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to create alerting file export")
|
||||
|
||||
@@ -19,23 +19,33 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
secrets_fakes "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
@@ -317,6 +327,14 @@ func TestProvisioningApi(t *testing.T) {
|
||||
rc.OrgID = 3
|
||||
rule := createTestAlertRule("rule", 1)
|
||||
|
||||
_, err := sut.folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: "folder-uid",
|
||||
Title: "Folder Title",
|
||||
OrgID: rc.OrgID,
|
||||
SignedInUser: &user.SignedInUser{OrgID: rc.OrgID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
response := sut.RoutePostAlertRule(&rc, rule)
|
||||
|
||||
require.Equal(t, 201, response.Status())
|
||||
@@ -330,7 +348,17 @@ func TestProvisioningApi(t *testing.T) {
|
||||
uid := util.GenerateShortUID()
|
||||
rule := createTestAlertRule("rule", 1)
|
||||
rule.UID = uid
|
||||
insertRuleInOrg(t, sut, rule, 3)
|
||||
|
||||
orgID := int64(3)
|
||||
_, err := sut.folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: "folder-uid",
|
||||
Title: "Folder Title",
|
||||
OrgID: orgID,
|
||||
SignedInUser: &user.SignedInUser{OrgID: orgID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
insertRuleInOrg(t, sut, rule, orgID)
|
||||
rc := createTestRequestCtx()
|
||||
rc.Req.Header = map[string][]string{"X-Disable-Provenance": {"hello"}}
|
||||
rc.OrgID = 3
|
||||
@@ -1614,6 +1642,7 @@ type testEnvironment struct {
|
||||
quotas provisioning.QuotaChecker
|
||||
prov provisioning.ProvisioningStore
|
||||
ac *recordingAccessControlFake
|
||||
user *user.SignedInUser
|
||||
rulesAuthz *fakes.FakeRuleService
|
||||
}
|
||||
|
||||
@@ -1639,20 +1668,8 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: string(raw),
|
||||
})
|
||||
sqlStore := db.InitTestDB(t)
|
||||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||
|
||||
// init folder service with default folder
|
||||
folderService := foldertest.NewFakeService()
|
||||
folderService.ExpectedFolder = &folder.Folder{}
|
||||
|
||||
store := store.DBstore{
|
||||
Logger: log,
|
||||
SQLStore: sqlStore,
|
||||
Cfg: setting.UnifiedAlertingSettings{
|
||||
BaseInterval: time.Second * 10,
|
||||
},
|
||||
FolderService: folderService,
|
||||
}
|
||||
quotas := &provisioning.MockQuotaChecker{}
|
||||
quotas.EXPECT().LimitOK()
|
||||
xact := &provisioning.NopTransactionManager{}
|
||||
@@ -1675,6 +1692,49 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
||||
}}, nil).Maybe()
|
||||
|
||||
ac := &recordingAccessControlFake{}
|
||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotatest.New(false, nil))
|
||||
require.NoError(t, err)
|
||||
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
folderService := folderimpl.ProvideService(actest.FakeAccessControl{}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore, sqlStore, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil)
|
||||
store := store.DBstore{
|
||||
Logger: log,
|
||||
SQLStore: sqlStore,
|
||||
Cfg: setting.UnifiedAlertingSettings{
|
||||
BaseInterval: time.Second * 10,
|
||||
},
|
||||
FolderService: folderService,
|
||||
}
|
||||
user := &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
/*
|
||||
Permissions: map[int64]map[string][]string{
|
||||
1: {dashboards.ActionFoldersCreate: {}, dashboards.ActionFoldersRead: {dashboards.ScopeFoldersAll}},
|
||||
},
|
||||
*/
|
||||
}
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
|
||||
parent, err := folderService.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: 1,
|
||||
UID: "folder-uid",
|
||||
Title: "Folder Title",
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = folderService.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: 1,
|
||||
UID: "folder-uid2",
|
||||
Title: "Folder Title2",
|
||||
ParentUID: parent.UID,
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ruleAuthz := &fakes.FakeRuleService{}
|
||||
|
||||
@@ -1689,6 +1749,7 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
||||
prov: prov,
|
||||
quotas: quotas,
|
||||
ac: ac,
|
||||
user: user,
|
||||
rulesAuthz: ruleAuthz,
|
||||
}
|
||||
}
|
||||
@@ -1710,7 +1771,8 @@ func createProvisioningSrvSutFromEnv(t *testing.T, env *testEnvironment) Provisi
|
||||
contactPointService: provisioning.NewContactPointService(env.configs, env.secrets, env.prov, env.xact, receiverSvc, env.log, env.store),
|
||||
templates: provisioning.NewTemplateService(env.configs, env.prov, env.xact, env.log),
|
||||
muteTimings: provisioning.NewMuteTimingService(env.configs, env.prov, env.xact, env.log),
|
||||
alertRules: provisioning.NewAlertRuleService(env.store, env.prov, env.folderService, env.dashboardService, env.quotas, env.xact, 60, 10, 100, env.log, &provisioning.NotificationSettingsValidatorProviderFake{}, env.rulesAuthz),
|
||||
alertRules: provisioning.NewAlertRuleService(env.store, env.prov, env.folderService, env.quotas, env.xact, 60, 10, 100, env.log, &provisioning.NotificationSettingsValidatorProviderFake{}, env.rulesAuthz),
|
||||
folderSvc: env.folderService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1725,6 +1787,9 @@ func createTestRequestCtx() contextmodel.ReqContext {
|
||||
},
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
1: {dashboards.ActionFoldersRead: {dashboards.ScopeFoldersAll}},
|
||||
},
|
||||
},
|
||||
Logger: &logtest.Fake{},
|
||||
}
|
||||
|
||||
@@ -35,9 +35,9 @@ func (srv RulerSrv) ExportFromPayload(c *contextmodel.ReqContext, ruleGroupConfi
|
||||
rules = append(rules, optional.AlertRule)
|
||||
}
|
||||
|
||||
groupsWithTitle := ngmodels.NewAlertRuleGroupWithFolderTitle(rules[0].GetGroupKey(), rules, namespace.Title)
|
||||
groupsWithFullpath := ngmodels.NewAlertRuleGroupWithFolderFullpath(rules[0].GetGroupKey(), rules, namespace.Fullpath)
|
||||
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle([]ngmodels.AlertRuleGroupWithFolderTitle{groupsWithTitle})
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderFullpath([]ngmodels.AlertRuleGroupWithFolderFullpath{groupsWithFullpath})
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to create alerting file export")
|
||||
}
|
||||
@@ -54,16 +54,16 @@ func (srv RulerSrv) ExportRules(c *contextmodel.ReqContext) response.Response {
|
||||
group := c.Query("group")
|
||||
uid := c.Query("ruleUid")
|
||||
|
||||
var groups []ngmodels.AlertRuleGroupWithFolderTitle
|
||||
var groups []ngmodels.AlertRuleGroupWithFolderFullpath
|
||||
if uid != "" {
|
||||
if group != "" || len(folderUIDs) > 0 {
|
||||
return ErrResp(http.StatusBadRequest, errors.New("group and folder should not be specified when a single rule is requested"), "")
|
||||
}
|
||||
rulesGroup, err := srv.getRuleWithFolderTitleByRuleUid(c, uid)
|
||||
rulesGroup, err := srv.getRuleWithFolderFullpathByRuleUid(c, uid)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
}
|
||||
groups = []ngmodels.AlertRuleGroupWithFolderTitle{rulesGroup}
|
||||
groups = []ngmodels.AlertRuleGroupWithFolderFullpath{rulesGroup}
|
||||
} else if group != "" {
|
||||
if len(folderUIDs) != 1 || folderUIDs[0] == "" {
|
||||
return ErrResp(http.StatusBadRequest,
|
||||
@@ -71,7 +71,7 @@ func (srv RulerSrv) ExportRules(c *contextmodel.ReqContext) response.Response {
|
||||
"",
|
||||
)
|
||||
}
|
||||
rulesGroup, err := srv.getRuleGroupWithFolderTitle(c, ngmodels.AlertRuleGroupKey{
|
||||
rulesGroup, err := srv.getRuleGroupWithFolderFullPath(c, ngmodels.AlertRuleGroupKey{
|
||||
OrgID: c.SignedInUser.GetOrgID(),
|
||||
NamespaceUID: folderUIDs[0],
|
||||
RuleGroup: group,
|
||||
@@ -79,10 +79,10 @@ func (srv RulerSrv) ExportRules(c *contextmodel.ReqContext) response.Response {
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
}
|
||||
groups = []ngmodels.AlertRuleGroupWithFolderTitle{rulesGroup}
|
||||
groups = []ngmodels.AlertRuleGroupWithFolderFullpath{rulesGroup}
|
||||
} else {
|
||||
var err error
|
||||
groups, err = srv.getRulesWithFolderTitleInFolders(c, folderUIDs)
|
||||
groups, err = srv.getRulesWithFolderFullPathInFolders(c, folderUIDs)
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
}
|
||||
@@ -95,45 +95,45 @@ func (srv RulerSrv) ExportRules(c *contextmodel.ReqContext) response.Response {
|
||||
// sort result so the response is always stable
|
||||
ngmodels.SortAlertRuleGroupWithFolderTitle(groups)
|
||||
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle(groups)
|
||||
e, err := AlertingFileExportFromAlertRuleGroupWithFolderFullpath(groups)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to create alerting file export")
|
||||
}
|
||||
return exportResponse(c, e)
|
||||
}
|
||||
|
||||
// getRuleWithFolderTitleByRuleUid calls getAuthorizedRuleByUid and combines its result with folder (aka namespace) title.
|
||||
func (srv RulerSrv) getRuleWithFolderTitleByRuleUid(c *contextmodel.ReqContext, ruleUID string) (ngmodels.AlertRuleGroupWithFolderTitle, error) {
|
||||
// getRuleWithFolderFullpathByRuleUid calls getAuthorizedRuleByUid and combines its result with folder (aka namespace) title.
|
||||
func (srv RulerSrv) getRuleWithFolderFullpathByRuleUid(c *contextmodel.ReqContext, ruleUID string) (ngmodels.AlertRuleGroupWithFolderFullpath, error) {
|
||||
rule, err := srv.getAuthorizedRuleByUid(c.Req.Context(), c, ruleUID)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, err
|
||||
return ngmodels.AlertRuleGroupWithFolderFullpath{}, err
|
||||
}
|
||||
namespace, err := srv.store.GetNamespaceByUID(c.Req.Context(), rule.NamespaceUID, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, errors.Join(errFolderAccess, err)
|
||||
return ngmodels.AlertRuleGroupWithFolderFullpath{}, errors.Join(errFolderAccess, err)
|
||||
}
|
||||
return ngmodels.NewAlertRuleGroupWithFolderTitle(rule.GetGroupKey(), []ngmodels.AlertRule{rule}, namespace.Title), nil
|
||||
return ngmodels.NewAlertRuleGroupWithFolderFullpath(rule.GetGroupKey(), []ngmodels.AlertRule{rule}, namespace.Fullpath), nil
|
||||
}
|
||||
|
||||
// getRuleGroupWithFolderTitle calls getAuthorizedRuleGroup and combines its result with folder (aka namespace) title.
|
||||
func (srv RulerSrv) getRuleGroupWithFolderTitle(c *contextmodel.ReqContext, ruleGroupKey ngmodels.AlertRuleGroupKey) (ngmodels.AlertRuleGroupWithFolderTitle, error) {
|
||||
// getRuleGroupWithFolderFullPath calls getAuthorizedRuleGroup and combines its result with folder (aka namespace) title.
|
||||
func (srv RulerSrv) getRuleGroupWithFolderFullPath(c *contextmodel.ReqContext, ruleGroupKey ngmodels.AlertRuleGroupKey) (ngmodels.AlertRuleGroupWithFolderFullpath, error) {
|
||||
namespace, err := srv.store.GetNamespaceByUID(c.Req.Context(), ruleGroupKey.NamespaceUID, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, errors.Join(errFolderAccess, err)
|
||||
return ngmodels.AlertRuleGroupWithFolderFullpath{}, errors.Join(errFolderAccess, err)
|
||||
}
|
||||
rules, err := srv.getAuthorizedRuleGroup(c.Req.Context(), c, ruleGroupKey)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, err
|
||||
return ngmodels.AlertRuleGroupWithFolderFullpath{}, err
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, ngmodels.ErrAlertRuleNotFound
|
||||
return ngmodels.AlertRuleGroupWithFolderFullpath{}, ngmodels.ErrAlertRuleNotFound
|
||||
}
|
||||
return ngmodels.NewAlertRuleGroupWithFolderTitleFromRulesGroup(ruleGroupKey, rules, namespace.Title), nil
|
||||
return ngmodels.NewAlertRuleGroupWithFolderFullpathFromRulesGroup(ruleGroupKey, rules, namespace.Fullpath), nil
|
||||
}
|
||||
|
||||
// getRulesWithFolderTitleInFolders gets list of folders to which user has access, and then calls searchAuthorizedAlertRules.
|
||||
// getRulesWithFolderFullPathInFolders gets list of folders to which user has access, and then calls searchAuthorizedAlertRules.
|
||||
// If argument folderUIDs is not empty it intersects it with the list of folders available for user and then retrieves rules that are in those folders.
|
||||
func (srv RulerSrv) getRulesWithFolderTitleInFolders(c *contextmodel.ReqContext, folderUIDs []string) ([]ngmodels.AlertRuleGroupWithFolderTitle, error) {
|
||||
func (srv RulerSrv) getRulesWithFolderFullPathInFolders(c *contextmodel.ReqContext, folderUIDs []string) ([]ngmodels.AlertRuleGroupWithFolderFullpath, error) {
|
||||
folders, err := srv.store.GetUserVisibleNamespaces(c.Req.Context(), c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -165,13 +165,13 @@ func (srv RulerSrv) getRulesWithFolderTitleInFolders(c *contextmodel.ReqContext,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]ngmodels.AlertRuleGroupWithFolderTitle, 0, len(rulesByGroup))
|
||||
result := make([]ngmodels.AlertRuleGroupWithFolderFullpath, 0, len(rulesByGroup))
|
||||
for groupKey, rulesGroup := range rulesByGroup {
|
||||
namespace, ok := folders[groupKey.NamespaceUID]
|
||||
if !ok {
|
||||
continue // user does not have access
|
||||
}
|
||||
result = append(result, ngmodels.NewAlertRuleGroupWithFolderTitleFromRulesGroup(groupKey, rulesGroup, namespace.Title))
|
||||
result = append(result, ngmodels.NewAlertRuleGroupWithFolderFullpathFromRulesGroup(groupKey, rulesGroup, namespace.Fullpath))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -31,8 +31,9 @@ var testData embed.FS
|
||||
func TestExportFromPayload(t *testing.T) {
|
||||
orgID := int64(1)
|
||||
folder := &folder2.Folder{
|
||||
UID: "e4584834-1a87-4dff-8913-8a4748dfca79",
|
||||
Title: "foo bar",
|
||||
UID: "e4584834-1a87-4dff-8913-8a4748dfca79",
|
||||
Title: "foo bar",
|
||||
Fullpath: "foo bar",
|
||||
}
|
||||
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
@@ -405,12 +406,12 @@ func TestExportRules(t *testing.T) {
|
||||
if tc.expectedStatus != 200 {
|
||||
return
|
||||
}
|
||||
var exp []ngmodels.AlertRuleGroupWithFolderTitle
|
||||
var exp []ngmodels.AlertRuleGroupWithFolderFullpath
|
||||
gr := ngmodels.GroupByAlertRuleGroupKey(tc.expectedRules)
|
||||
for key, rules := range gr {
|
||||
folder, err := ruleStore.GetNamespaceByUID(context.Background(), key.NamespaceUID, orgID, nil)
|
||||
require.NoError(t, err)
|
||||
exp = append(exp, ngmodels.NewAlertRuleGroupWithFolderTitleFromRulesGroup(key, rules, folder.Title))
|
||||
exp = append(exp, ngmodels.NewAlertRuleGroupWithFolderFullpathFromRulesGroup(key, rules, folder.Fullpath))
|
||||
}
|
||||
sort.SliceStable(exp, func(i, j int) bool {
|
||||
gi, gj := exp[i], exp[j]
|
||||
@@ -422,7 +423,7 @@ func TestExportRules(t *testing.T) {
|
||||
}
|
||||
return gi.Title < gj.Title
|
||||
})
|
||||
groups, err := AlertingFileExportFromAlertRuleGroupWithFolderTitle(exp)
|
||||
groups, err := AlertingFileExportFromAlertRuleGroupWithFolderFullpath(exp)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(exportResponse(rc, groups).Body()), string(resp.Body()))
|
||||
|
||||
@@ -135,11 +135,11 @@ func ApiAlertRuleGroupFromAlertRuleGroup(d models.AlertRuleGroup) definitions.Al
|
||||
}
|
||||
}
|
||||
|
||||
// AlertingFileExportFromAlertRuleGroupWithFolderTitle creates an definitions.AlertingFileExport DTO from []models.AlertRuleGroupWithFolderTitle.
|
||||
func AlertingFileExportFromAlertRuleGroupWithFolderTitle(groups []models.AlertRuleGroupWithFolderTitle) (definitions.AlertingFileExport, error) {
|
||||
// AlertingFileExportFromAlertRuleGroupWithFolderFullpath creates an definitions.AlertingFileExport DTO from []models.AlertRuleGroupWithFolderTitle.
|
||||
func AlertingFileExportFromAlertRuleGroupWithFolderFullpath(groups []models.AlertRuleGroupWithFolderFullpath) (definitions.AlertingFileExport, error) {
|
||||
f := definitions.AlertingFileExport{APIVersion: 1}
|
||||
for _, group := range groups {
|
||||
export, err := AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle(group)
|
||||
export, err := AlertRuleGroupExportFromAlertRuleGroupWithFolderFullpath(group)
|
||||
if err != nil {
|
||||
return definitions.AlertingFileExport{}, err
|
||||
}
|
||||
@@ -148,8 +148,8 @@ func AlertingFileExportFromAlertRuleGroupWithFolderTitle(groups []models.AlertRu
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle creates a definitions.AlertRuleGroupExport DTO from models.AlertRuleGroup.
|
||||
func AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle(d models.AlertRuleGroupWithFolderTitle) (definitions.AlertRuleGroupExport, error) {
|
||||
// AlertRuleGroupExportFromAlertRuleGroupWithFolderFullpath creates a definitions.AlertRuleGroupExport DTO from models.AlertRuleGroup.
|
||||
func AlertRuleGroupExportFromAlertRuleGroupWithFolderFullpath(d models.AlertRuleGroupWithFolderFullpath) (definitions.AlertRuleGroupExport, error) {
|
||||
rules := make([]definitions.AlertRuleExport, 0, len(d.Rules))
|
||||
for i := range d.Rules {
|
||||
alert, err := AlertRuleExportFromAlertRule(d.Rules[i])
|
||||
@@ -161,7 +161,7 @@ func AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle(d models.AlertRuleGro
|
||||
return definitions.AlertRuleGroupExport{
|
||||
OrgID: d.OrgID,
|
||||
Name: d.Title,
|
||||
Folder: d.FolderTitle,
|
||||
Folder: d.FolderFullpath,
|
||||
FolderUID: d.FolderUID,
|
||||
Interval: model.Duration(time.Duration(d.Interval) * time.Second),
|
||||
IntervalSeconds: d.Interval,
|
||||
|
||||
Reference in New Issue
Block a user