Dashboard Provisioning: Add duplicate cleanup for modes 0-2 (#113336)

This commit is contained in:
Stephanie Hingtgen
2025-11-02 20:39:15 +00:00
committed by GitHub
parent c98259f74a
commit 445e88cb93
8 changed files with 139 additions and 11 deletions
@@ -876,6 +876,12 @@ func (dr *DashboardServiceImpl) waitForSearchQuery(ctx context.Context, query *d
}
func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *dashboards.DeleteOrphanedProvisionedDashboardsCommand) error {
// cleanup duplicate provisioned dashboards first (this will have the same name and external_id)
// note: only works in modes 1-3
if err := dr.DeleteDuplicateProvisionedDashboards(ctx); err != nil {
dr.log.Error("Failed to delete duplicate provisioned dashboards", "error", err)
}
// check each org for orphaned provisioned dashboards
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
if err != nil {
@@ -914,6 +920,34 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.
return nil
}
func (dr *DashboardServiceImpl) DeleteDuplicateProvisionedDashboards(ctx context.Context) error {
duplicates, err := dr.dashboardStore.GetDuplicateProvisionedDashboards(ctx)
if err != nil {
return err
}
type provisioningKey struct {
name string
externalID string
}
groups := make(map[provisioningKey][]*dashboards.DashboardProvisioningSearchResults)
for _, dash := range duplicates {
key := provisioningKey{
name: dash.Provisioner,
externalID: dash.ExternalID,
}
if _, exists := groups[key]; exists {
if err = dr.deleteDashboard(ctx, dash.ID, dash.UID, dash.OrgID, false); err != nil {
dr.log.Error("Failed to delete duplicate provisioned dashboard", "error", err, "dashboardUID", dash.UID, "dashboardID", dash.ID)
}
}
groups[key] = append(groups[key], dash)
}
return nil
}
func (dr *DashboardServiceImpl) ValidateDashboardRefreshInterval(minRefreshInterval string, targetRefreshInterval string) error {
if minRefreshInterval == "" {
return nil
@@ -629,16 +629,19 @@ func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
fakeDashboardStore := &dashboards.FakeDashboardStore{}
service := &DashboardServiceImpl{
cfg: setting.NewCfg(),
orgService: &orgtest.FakeOrgService{
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
},
publicDashboardService: fakePublicDashboardService,
dashboardStore: fakeDashboardStore,
log: log.NewNopLogger(),
}
t.Run("Should delete across all orgs, but only delete file based provisioned dashboards", func(t *testing.T) {
fakeDashboardStore.On("GetDuplicateProvisionedDashboards", mock.Anything).Return([]*dashboards.DashboardProvisioningSearchResults{}, nil)
_, k8sCliMock := setupK8sDashboardTests(service)
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
@@ -783,6 +786,7 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
})
t.Run("Should retry until deleted dashboard not found in search", func(t *testing.T) {
fakeDashboardStore.On("GetDuplicateProvisionedDashboards", mock.Anything).Return([]*dashboards.DashboardProvisioningSearchResults{}, nil)
repo := "test"
singleOrgService := &DashboardServiceImpl{
cfg: setting.NewCfg(),
@@ -790,6 +794,7 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
},
publicDashboardService: fakePublicDashboardService,
dashboardStore: fakeDashboardStore,
log: log.NewNopLogger(),
}
ctx, k8sCliMock := setupK8sDashboardTests(singleOrgService)
@@ -876,6 +881,7 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
})
t.Run("Will not wait for indexer when no dashboards were deleted", func(t *testing.T) {
fakeDashboardStore.On("GetDuplicateProvisionedDashboards", mock.Anything).Return([]*dashboards.DashboardProvisioningSearchResults{}, nil)
repo := "test"
singleOrgService := &DashboardServiceImpl{
cfg: setting.NewCfg(),
@@ -883,6 +889,7 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
},
publicDashboardService: fakePublicDashboardService,
dashboardStore: fakeDashboardStore,
log: log.NewNopLogger(),
}
ctx, k8sCliMock := setupK8sDashboardTests(singleOrgService)