From 1cffc51ba0efb555e6b03a7f563e5515297f5e0f Mon Sep 17 00:00:00 2001 From: leonorfmartins Date: Mon, 9 Dec 2024 16:40:30 +0100 Subject: [PATCH] Add alert store to folder api builder --- pkg/api/folder.go | 56 +++++------------------- pkg/services/ngalert/store/alert_rule.go | 10 +++++ 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 5a447ffde41..439f7b9003f 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -19,7 +19,6 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" folderalpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1" - "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/slugify" internalfolders "github.com/grafana/grafana/pkg/registry/apis/folders" @@ -33,6 +32,7 @@ import ( "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/libraryelements/model" + ngstore "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/util" @@ -645,7 +645,7 @@ type folderK8sHandler struct { // #TODO check if it makes more sense to move this to FolderAPIBuilder accesscontrolService accesscontrol.Service userService user.Service - kvstore *kvstore.NamespacedKVStore + ngAlertSvc ngstore.DBstore } //----------------------------------------------------------------------------------------- @@ -653,13 +653,14 @@ type folderK8sHandler struct { //----------------------------------------------------------------------------------------- func newFolderK8sHandler(hs *HTTPServer) *folderK8sHandler { + return &folderK8sHandler{ gvr: folderalpha1.FolderResourceInfo.GroupVersionResource(), namespacer: request.GetNamespaceMapper(hs.Cfg), clientConfigProvider: hs.clientConfigProvider, accesscontrolService: hs.accesscontrolService, userService: hs.userService, - kvstore: kvstore.WithNamespace(hs.kvStore, 0, "storage.dualwriting"), + ngAlertSvc: hs.ngAlertSvc, } } @@ -780,16 +781,11 @@ func (fk8s *folderK8sHandler) deleteFolder(c *contextmodel.ReqContext) { ctx := c.Req.Context() - hasDependants, err := hasDependants(ctx, fk8s.kvstore) + err := fk8s.checkForAlertRules() if err != nil { fk8s.writeError(c, err) return } - - if hasDependants { - fk8s.writeError(c, folder.ErrFolderNotEmpty.Errorf("this folder is not empty. Please make sure it does not contain any dashboards, alerts, library panels or SLOs")) - return - } if err := client.Delete(ctx, uid, v1.DeleteOptions{}); err != nil { fk8s.writeError(c, err) return @@ -838,40 +834,6 @@ func (fk8s *folderK8sHandler) updateFolder(c *contextmodel.ReqContext) { // Utility functions //----------------------------------------------------------------------------------------- -func hasDependants(ctx context.Context, kvstore *kvstore.NamespacedKVStore) (bool, error) { - // TODO: how can we check for alert rules and library panels? - var folderDependants = []string{ - folderalpha1.RESOURCEGROUP, - "dashboards.dashboard.grafana.app", - "librarypanels.librarypanel.grafana.app", - "alertrules.alertmanager.grafana.app", - } - var hasDependants bool - for _, resource := range folderDependants { - item, found, err := kvstore.Get(ctx, resource) - if err != nil { - return false, err - } - if found { - mode, err := strconv.Atoi(item) - if err != nil { - return false, err - } - // if dual writer mode > 2, it means the source of truth is unified store - if mode > 2 { - // call unified store count - fmt.Println("counting dependants in unified store") - } - // if not found or mode < 3 call legacy store count - } else { - fmt.Println("counting dependants in legacy store") - } - } - - // TODO: also check for SLORules in another store - return hasDependants, nil -} - func (fk8s *folderK8sHandler) getClient(c *contextmodel.ReqContext) (dynamic.ResourceInterface, bool) { dyn, err := dynamic.NewForConfig(fk8s.clientConfigProvider.GetDirectRestConfig(c)) if err != nil { @@ -1037,7 +999,7 @@ func (fk8s *folderK8sHandler) getFolderACMetadata(c *contextmodel.ReqContext, f return nil, nil } - folderIDs, err := fk8s.getParents(f) + folderIDs, err := getParents(f) if err != nil { return nil, err } @@ -1053,7 +1015,11 @@ func (fk8s *folderK8sHandler) getFolderACMetadata(c *contextmodel.ReqContext, f return metadata, nil } -func (fk8s *folderK8sHandler) getParents(f *folder.Folder) (map[string]bool, error) { +func (fk8s *folderK8sHandler) checkForAlertRules() error { + return folder.ErrFolderNotEmpty.Errorf("folder contains alert rules, please delete them first.") +} + +func getParents(f *folder.Folder) (map[string]bool, error) { folderIDs := map[string]bool{f.UID: true} if (f.UID == accesscontrol.GeneralFolderUID) || (f.UID == folder.SharedWithMeFolderUID) { return folderIDs, nil diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index bbc64fe04cb..2b93a830ecc 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -445,6 +445,16 @@ func (st DBstore) CountInFolders(ctx context.Context, orgID int64, folderUIDs [] return count, err } +func (st DBstore) FolderHasAlertRules(ctx context.Context, orgID int64, folderUID string) (bool, error) { + var count int64 + var err error + err = st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error { + count, err = sess.Table("alert_rule").Where("org_id = ?", orgID).Where("namespace_uid = ?", folderUID).Count() + return err + }) + return count > 0, err +} + // ListAlertRules is a handler for retrieving alert rules of specific organisation. func (st DBstore) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (result ngmodels.RulesGroup, err error) { err = st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error {