280 lines
10 KiB
Go
280 lines
10 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func Test_canBeAutomaticallyCleanedUp(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
configs []dashboards.ProvisioningConfig
|
|
expectedSkip string
|
|
}{
|
|
{
|
|
name: "no dashboards defined in the configuration",
|
|
configs: []dashboards.ProvisioningConfig{},
|
|
expectedSkip: "no provisioned dashboards",
|
|
},
|
|
{
|
|
name: "first defined dashboard has no folder defined",
|
|
configs: []dashboards.ProvisioningConfig{
|
|
{Name: "1", Folder: ""},
|
|
{Folder: "f1"},
|
|
},
|
|
expectedSkip: "dashboard has no folder: 1",
|
|
},
|
|
{
|
|
name: "one of the provisioned dashboards has no folder defined",
|
|
configs: []dashboards.ProvisioningConfig{
|
|
{Name: "1", Folder: "f1"},
|
|
{Name: "2", Folder: "f1"},
|
|
{Name: "3", Folder: ""},
|
|
{Name: "4", Folder: "f1"},
|
|
},
|
|
expectedSkip: "dashboards provisioned across multiple folders",
|
|
},
|
|
{
|
|
name: "one of the provisioned dashboards allows UI updates",
|
|
configs: []dashboards.ProvisioningConfig{
|
|
{Name: "1", Folder: "f1"},
|
|
{Name: "2", Folder: "f1", AllowUIUpdates: true},
|
|
{Name: "3", Folder: "f1"},
|
|
{Name: "4", Folder: "f1"},
|
|
},
|
|
expectedSkip: "contains dashboards with allowUiUpdates",
|
|
},
|
|
{
|
|
name: "one of the provisioned dashboards is in a different folder",
|
|
configs: []dashboards.ProvisioningConfig{
|
|
{Name: "1", Folder: "f1"},
|
|
{Name: "2", Folder: "f1"},
|
|
{Name: "3", Folder: "f1"},
|
|
{Name: "4", Folder: "different"},
|
|
},
|
|
expectedSkip: "dashboards provisioned across multiple folders",
|
|
},
|
|
{
|
|
name: "can be skipped when all conditions are met",
|
|
configs: []dashboards.ProvisioningConfig{
|
|
{Name: "1", Folder: "f1"},
|
|
{Name: "2", Folder: "f1"},
|
|
{Name: "3", Folder: "f1"},
|
|
{Name: "4", Folder: "f1"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedSkip, canBeAutomaticallyCleanedUp(tc.configs))
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_cleanupSteps(t *testing.T) {
|
|
isDashboard, isFolder := false, true
|
|
|
|
fromUser := func(uid, name string, isFolder bool) dashboards.DashboardSearchProjection {
|
|
return dashboards.DashboardSearchProjection{
|
|
UID: uid,
|
|
ManagerId: name,
|
|
IsFolder: isFolder,
|
|
}
|
|
}
|
|
|
|
provisioned := func(uid, name string, isFolder bool) dashboards.DashboardSearchProjection {
|
|
dashboard := fromUser(uid, name, isFolder)
|
|
dashboard.ManagedBy = utils.ManagerKindClassicFP //nolint:staticcheck
|
|
return dashboard
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
provisionedFolders []string
|
|
provisionedResources []dashboards.DashboardSearchProjection
|
|
configDashboards []string
|
|
expectedSteps []deleteProvisionedResource
|
|
expectedErr string
|
|
}{
|
|
{
|
|
name: "no provisioned folders, nothing to do",
|
|
provisionedFolders: []string{},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
},
|
|
},
|
|
{
|
|
name: "multiple folders, a user-created dashboard in one of them",
|
|
provisionedFolders: []string{"folder1", "folder2"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
fromUser("d3", "User1", isDashboard),
|
|
provisioned("d4", "Provisioned3", isDashboard),
|
|
},
|
|
expectedErr: "multiple provisioning folders exist with at least one user-created resource",
|
|
},
|
|
{
|
|
name: "multiple folders, a user-created folder in one of them",
|
|
provisionedFolders: []string{"folder1", "folder2"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3", "Provisioned4"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
provisioned("d3", "Provisioned3", isDashboard),
|
|
fromUser("f1", "UserFolder1", isFolder),
|
|
},
|
|
expectedErr: "multiple provisioning folders exist with at least one user-created resource",
|
|
},
|
|
{
|
|
name: "single folder, some dashboards duplicated",
|
|
provisionedFolders: []string{"folder1"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3", "Provisioned4"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
// Provisioned1 is duplicated.
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
provisioned("d3", "Provisioned1", isDashboard),
|
|
provisioned("d4", "Provisioned3", isDashboard),
|
|
},
|
|
expectedSteps: []deleteProvisionedResource{
|
|
{Type: searchstore.TypeDashboard, UID: "d1"},
|
|
{Type: searchstore.TypeDashboard, UID: "d2"},
|
|
{Type: searchstore.TypeDashboard, UID: "d3"},
|
|
{Type: searchstore.TypeDashboard, UID: "d4"},
|
|
},
|
|
},
|
|
{
|
|
name: "single folder, duplicated dashboards, user-created dashboards are ignored",
|
|
provisionedFolders: []string{"folder1"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3", "Provisioned4"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
// Provisioned1 is duplicated.
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
fromUser("d3", "User1", isDashboard),
|
|
provisioned("d4", "Provisioned3", isDashboard),
|
|
provisioned("d5", "Provisioned1", isDashboard),
|
|
},
|
|
// User dashboard (d3) is not deleted.
|
|
expectedSteps: []deleteProvisionedResource{
|
|
{Type: searchstore.TypeDashboard, UID: "d1"},
|
|
{Type: searchstore.TypeDashboard, UID: "d2"},
|
|
{Type: searchstore.TypeDashboard, UID: "d4"},
|
|
{Type: searchstore.TypeDashboard, UID: "d5"},
|
|
},
|
|
},
|
|
{
|
|
name: "single folder, duplicated dashboards, user-created folders are ignored",
|
|
provisionedFolders: []string{"folder1"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
// Provisioned1 is duplicated.
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
provisioned("d3", "Provisioned3", isDashboard),
|
|
provisioned("d4", "Provisioned1", isDashboard),
|
|
fromUser("f1", "UserFolder1", isFolder),
|
|
},
|
|
// User folder (f1) is not deleted.
|
|
expectedSteps: []deleteProvisionedResource{
|
|
{Type: searchstore.TypeDashboard, UID: "d1"},
|
|
{Type: searchstore.TypeDashboard, UID: "d2"},
|
|
{Type: searchstore.TypeDashboard, UID: "d3"},
|
|
{Type: searchstore.TypeDashboard, UID: "d4"},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple folders, only provisioned dashboards",
|
|
provisionedFolders: []string{"folder1", "folder2"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3", "Provisioned4"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
provisioned("d3", "Provisioned3", isDashboard),
|
|
provisioned("d4", "Provisioned4", isDashboard),
|
|
},
|
|
// Delete all dashboards, then all folders.
|
|
expectedSteps: []deleteProvisionedResource{
|
|
{Type: searchstore.TypeDashboard, UID: "d1"},
|
|
{Type: searchstore.TypeDashboard, UID: "d2"},
|
|
{Type: searchstore.TypeDashboard, UID: "d3"},
|
|
{Type: searchstore.TypeDashboard, UID: "d4"},
|
|
{Type: searchstore.TypeFolder, UID: "folder1"},
|
|
{Type: searchstore.TypeFolder, UID: "folder2"},
|
|
},
|
|
},
|
|
{
|
|
name: "single folder, only deletes dashboards defined in the config file",
|
|
provisionedFolders: []string{"folder1"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
provisioned("d3", "Provisioned1", isDashboard),
|
|
provisioned("d4", "Provisioned4", isDashboard),
|
|
provisioned("d5", "Provisioned4", isDashboard),
|
|
},
|
|
// Delete duplicated dashboards, but keep Provisioned4, since it's not in the config file.
|
|
expectedSteps: []deleteProvisionedResource{
|
|
{Type: searchstore.TypeDashboard, UID: "d1"},
|
|
{Type: searchstore.TypeDashboard, UID: "d2"},
|
|
{Type: searchstore.TypeDashboard, UID: "d3"},
|
|
},
|
|
},
|
|
{
|
|
name: "single folder, no duplicated dashboards",
|
|
provisionedFolders: []string{"folder1"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3", "Provisioned4"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
provisioned("d3", "Provisioned3", isDashboard),
|
|
provisioned("d4", "Provisioned4", isDashboard),
|
|
},
|
|
expectedSteps: nil, // no duplicates, nothing to do
|
|
},
|
|
{
|
|
name: "single folder, no duplicated dashboards, multiple user-created resources",
|
|
provisionedFolders: []string{"folder1"},
|
|
configDashboards: []string{"Provisioned1", "Provisioned2", "Provisioned3", "Provisioned4"},
|
|
provisionedResources: []dashboards.DashboardSearchProjection{
|
|
provisioned("d1", "Provisioned1", isDashboard),
|
|
provisioned("d2", "Provisioned2", isDashboard),
|
|
fromUser("f1", "UserFolder1", isFolder),
|
|
provisioned("d3", "Provisioned3", isDashboard),
|
|
fromUser("d4", "User1", isDashboard),
|
|
provisioned("d5", "Provisioned4", isDashboard),
|
|
fromUser("d6", "User2", isDashboard),
|
|
fromUser("f2", "UserFolder2", isFolder),
|
|
},
|
|
expectedSteps: nil, // no duplicates in the provisioned set, nothing to do
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
provisionedSet := make(map[string]bool)
|
|
for _, name := range tc.configDashboards {
|
|
provisionedSet[name] = true
|
|
}
|
|
|
|
steps, err := cleanupSteps(tc.provisionedFolders, tc.provisionedResources, provisionedSet)
|
|
if tc.expectedErr == "" {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expectedSteps, steps)
|
|
} else {
|
|
require.Error(t, err)
|
|
require.Equal(t, tc.expectedErr, err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|