fix: delete subfolder dangling panels (#113419)
* fix: delete subfolder dangling panels and error if used * chore: add observation about library panel DeleteInFolders - logs folders UIDs on DeleteInFolders error * chore: add integration test for blocking library panel deletion and handling dangling library panels * chore: fix integration test on mode 4 and 5
This commit is contained in:
@@ -644,6 +644,21 @@ func (s *Service) deleteFromApiServer(ctx context.Context, cmd *folder.DeleteFol
|
||||
return folder.ErrFolderNotEmpty.Errorf("folder contains %d alert rules", alertRulesInFolder)
|
||||
}
|
||||
|
||||
libraryPanelSrv, ok := s.registry[entity.StandardKindLibraryPanel]
|
||||
if !ok {
|
||||
return folder.ErrInternal.Errorf("no library panel service found in registry")
|
||||
}
|
||||
// /* TODO: after a decision regarding folder deletion permissions has been made
|
||||
// (https://github.com/grafana/grafana-enterprise/issues/5144),
|
||||
// remove the following call to DeleteInFolders
|
||||
// and remove "user" from the signature of DeleteInFolder in the folder RegistryService.
|
||||
// Context: https://github.com/grafana/grafana/pull/69149#discussion_r1235057903
|
||||
// */
|
||||
// Obs: DeleteInFolders only deletes dangling library panels (not linked to any dashboard) and throws errors if there are connections
|
||||
if err := libraryPanelSrv.DeleteInFolders(ctx, cmd.OrgID, folders, cmd.SignedInUser); err != nil {
|
||||
s.log.Error("failed to delete dangling library panels in folders", "error", err, "folders", strings.Join(folders, ","))
|
||||
return err
|
||||
}
|
||||
// We need a list of dashboard uids inside the folder to delete related dashboards & public dashboards -
|
||||
// we cannot use the dashboard service directly due to circular dependencies, so use the search client to get the dashboards
|
||||
request := &resourcepb.ResourceSearchRequest{
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
clientrest "k8s.io/client-go/rest"
|
||||
|
||||
folderv1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -33,6 +34,9 @@ import (
|
||||
dashboardsearch "github.com/grafana/grafana/pkg/services/dashboards/service/search"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
||||
"github.com/grafana/grafana/pkg/services/search/model"
|
||||
@@ -209,6 +213,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
|
||||
{Action: dashboards.ActionFoldersDelete, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: accesscontrol.ActionAlertingRuleDelete, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: accesscontrol.ActionLibraryPanelsDelete, Scope: dashboards.ScopeFoldersAll},
|
||||
}),
|
||||
}}
|
||||
|
||||
@@ -219,6 +224,16 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
|
||||
AccessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
}
|
||||
|
||||
mockDashboardService := dashboards.NewFakeDashboardService(t)
|
||||
mockFolderService := foldertest.NewFakeService()
|
||||
elementService := libraryelements.ProvideService(cfg, db, routing.NewRouteRegister(), mockFolderService, featuremgmt.WithFeatures(), actest.FakeAccessControl{ExpectedEvaluate: true}, mockDashboardService, nil, nil)
|
||||
lps := librarypanels.LibraryPanelService{
|
||||
Cfg: cfg,
|
||||
SQLStore: db,
|
||||
LibraryElementService: elementService,
|
||||
FolderService: mockFolderService,
|
||||
}
|
||||
|
||||
publicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
||||
|
||||
fakeK8sClient := new(client.MockK8sHandler)
|
||||
@@ -237,6 +252,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
|
||||
}
|
||||
|
||||
require.NoError(t, folderService.RegisterService(alertingStore))
|
||||
require.NoError(t, folderService.RegisterService(lps))
|
||||
|
||||
t.Run("Folder service tests", func(t *testing.T) {
|
||||
t.Run("Given user has no permissions", func(t *testing.T) {
|
||||
@@ -877,6 +893,17 @@ func TestIntegrationDeleteFoldersFromApiServer(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, service.RegisterService(alertingStore))
|
||||
|
||||
mockDashboardService := dashboards.NewFakeDashboardService(t)
|
||||
mockFolderService := foldertest.NewFakeService()
|
||||
elementService := libraryelements.ProvideService(cfg, db, routing.NewRouteRegister(), mockFolderService, featuremgmt.WithFeatures(), actest.FakeAccessControl{ExpectedEvaluate: true}, mockDashboardService, nil, nil)
|
||||
lps := librarypanels.LibraryPanelService{
|
||||
Cfg: cfg,
|
||||
SQLStore: db,
|
||||
LibraryElementService: elementService,
|
||||
FolderService: mockFolderService,
|
||||
}
|
||||
require.NoError(t, service.RegisterService(lps))
|
||||
|
||||
t.Run("Should delete folder", func(t *testing.T) {
|
||||
publicDashboardFakeService.On("DeleteByDashboardUIDs", mock.Anything, int64(1), []string{}).Return(nil).Once()
|
||||
dashboardK8sclient.On("Search", mock.Anything, int64(1), mock.Anything).Return(&resourcepb.ResourceSearchResponse{Results: &resourcepb.ResourceTable{}}, nil).Once()
|
||||
|
||||
Reference in New Issue
Block a user