mirror of https://github.com/grafana/grafana.git
2120 lines
76 KiB
Go
2120 lines
76 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
dashboardv0alpha1 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"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/guardian"
|
|
"github.com/grafana/grafana/pkg/services/org"
|
|
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
|
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
|
"github.com/grafana/grafana/pkg/services/quota"
|
|
"github.com/grafana/grafana/pkg/services/search/model"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
|
)
|
|
|
|
func TestDashboardService(t *testing.T) {
|
|
t.Run("Dashboard service tests", func(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
|
defer fakeStore.AssertExpectations(t)
|
|
|
|
folderSvc := foldertest.NewFakeService()
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
log: log.New("test.logger"),
|
|
dashboardStore: &fakeStore,
|
|
folderService: folderSvc,
|
|
features: featuremgmt.WithFeatures(),
|
|
publicDashboardService: fakePublicDashboardService,
|
|
}
|
|
folderStore := foldertest.FakeFolderStore{}
|
|
folderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(nil, dashboards.ErrFolderNotFound).Once()
|
|
service.folderStore = &folderStore
|
|
|
|
origNewDashboardGuardian := guardian.New
|
|
defer func() { guardian.New = origNewDashboardGuardian }()
|
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
|
|
|
t.Run("Save dashboard validation", func(t *testing.T) {
|
|
dto := &dashboards.SaveDashboardDTO{}
|
|
|
|
t.Run("When saving a dashboard with empty title it should return error", func(t *testing.T) {
|
|
titles := []string{"", " ", " \t "}
|
|
|
|
for _, title := range titles {
|
|
dto.Dashboard = dashboards.NewDashboard(title)
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
|
require.Equal(t, err, dashboards.ErrDashboardTitleEmpty)
|
|
}
|
|
})
|
|
|
|
t.Run("Should return validation error if message is too long", func(t *testing.T) {
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
dto.Message = `Here we go, 500+ characters for testing. I'm sorry that you're
|
|
having to read this. I spent too long trying to come up with something clever
|
|
to say or a funny joke. Unforuntately, nothing came to mind. So instead, I'm
|
|
will share this with you, as a form of payment for having to read this:
|
|
https://youtu.be/dQw4w9WgXcQ?si=KeoTIpn9tUtQnOBk! Enjoy :) Now lets see if
|
|
this test passes or if the result is more exciting than these 500 characters
|
|
I wrote. Best of luck to the both of us!`
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
|
require.Equal(t, err, dashboards.ErrDashboardMessageTooLong)
|
|
|
|
// set to a shorter message for the rest of the tests
|
|
dto.Message = `message`
|
|
})
|
|
|
|
t.Run("Should return validation error if folder is named General", func(t *testing.T) {
|
|
dto.Dashboard = dashboards.NewDashboardFolder("General")
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
|
require.Equal(t, err, dashboards.ErrDashboardFolderNameExists)
|
|
})
|
|
|
|
t.Run("When saving a dashboard should validate uid", func(t *testing.T) {
|
|
testCases := []struct {
|
|
Uid string
|
|
Error error
|
|
}{
|
|
{Uid: "", Error: nil},
|
|
{Uid: " ", Error: nil},
|
|
{Uid: " \t ", Error: nil},
|
|
{Uid: "asdf90_-", Error: nil},
|
|
{Uid: "asdf/90", Error: dashboards.ErrDashboardInvalidUid},
|
|
{Uid: " asdfghjklqwertyuiopzxcvbnmasdfghjklqwer ", Error: nil},
|
|
{Uid: "asdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnmasdfghjklqwertyuiopzxcvbnm", Error: dashboards.ErrDashboardUidTooLong},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
dto.Dashboard = dashboards.NewDashboard("title")
|
|
dto.Dashboard.SetUID(tc.Uid)
|
|
dto.User = &user.SignedInUser{}
|
|
|
|
if tc.Error == nil {
|
|
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
|
}
|
|
_, err := service.BuildSaveDashboardCommand(context.Background(), dto, false)
|
|
require.Equal(t, err, tc.Error)
|
|
}
|
|
})
|
|
|
|
t.Run("Should return validation error if a folder that is specified can't be found", func(t *testing.T) {
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
dto.Dashboard.FolderUID = "non-existing-folder"
|
|
folderSvc := foldertest.FakeService{ExpectedError: dashboards.ErrFolderNotFound}
|
|
service.folderService = &folderSvc
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
|
require.Equal(t, err, dashboards.ErrFolderNotFound)
|
|
})
|
|
|
|
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
|
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
dto.Dashboard.SetID(3)
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
|
_, err := service.SaveDashboard(context.Background(), dto, false)
|
|
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
|
})
|
|
|
|
t.Run("Should not return validation error if dashboard is provisioned but UI updates allowed", func(t *testing.T) {
|
|
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
|
fakeStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
dto.Dashboard.SetID(3)
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
|
_, err := service.SaveDashboard(context.Background(), dto, true)
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
|
|
t.Run("Save provisioned dashboard validation", func(t *testing.T) {
|
|
dto := &dashboards.SaveDashboardDTO{}
|
|
|
|
t.Run("Should not return validation error if dashboard is provisioned", func(t *testing.T) {
|
|
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.AnythingOfType("*dashboards.DashboardProvisioning")).Return(nil).Once()
|
|
fakeStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
dto.Dashboard.SetID(3)
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
|
_, err := service.SaveProvisionedDashboard(context.Background(), dto, nil)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Should override invalid refresh interval if dashboard is provisioned", func(t *testing.T) {
|
|
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.AnythingOfType("*dashboards.DashboardProvisioning")).Return(nil).Once()
|
|
fakeStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
|
|
|
oldRefreshInterval := service.cfg.MinRefreshInterval
|
|
service.cfg.MinRefreshInterval = "5m"
|
|
defer func() { service.cfg.MinRefreshInterval = oldRefreshInterval }()
|
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
dto.Dashboard.SetID(3)
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
|
dto.Dashboard.Data.Set("refresh", "1s")
|
|
_, err := service.SaveProvisionedDashboard(context.Background(), dto, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, dto.Dashboard.Data.Get("refresh").MustString(), "5m")
|
|
})
|
|
})
|
|
|
|
t.Run("Import dashboard validation", func(t *testing.T) {
|
|
dto := &dashboards.SaveDashboardDTO{}
|
|
|
|
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
|
|
|
dto.Dashboard = dashboards.NewDashboard("Dash")
|
|
dto.Dashboard.SetID(3)
|
|
dto.User = &user.SignedInUser{UserID: 1}
|
|
_, err := service.ImportDashboard(context.Background(), dto)
|
|
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
|
})
|
|
})
|
|
|
|
t.Run("Given provisioned dashboard", func(t *testing.T) {
|
|
t.Run("DeleteProvisionedDashboard should delete it", func(t *testing.T) {
|
|
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
|
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("DeleteDashboard should fail to delete it when provisioning information is missing", func(t *testing.T) {
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
|
err := service.DeleteDashboard(context.Background(), 1, "", 1)
|
|
require.Equal(t, err, dashboards.ErrDashboardCannotDeleteProvisionedDashboard)
|
|
})
|
|
})
|
|
|
|
t.Run("Given non provisioned dashboard", func(t *testing.T) {
|
|
t.Run("DeleteProvisionedDashboard should delete the dashboard", func(t *testing.T) {
|
|
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
|
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("DeleteDashboard should delete it", func(t *testing.T) {
|
|
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
|
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(nil, nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
err := service.DeleteDashboard(context.Background(), 1, "", 1)
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
|
|
t.Run("Count dashboards in folder", func(t *testing.T) {
|
|
fakeStore.On("CountDashboardsInFolders", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil)
|
|
folderSvc.ExpectedFolder = &folder.Folder{UID: "i am a folder"}
|
|
// set up a ctx with signed in user
|
|
usr := &user.SignedInUser{UserID: 1}
|
|
ctx := identity.WithRequester(context.Background(), usr)
|
|
|
|
count, err := service.CountInFolders(ctx, 1, []string{"i am a folder"}, usr)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(3), count)
|
|
})
|
|
|
|
t.Run("Delete dashboards in folder", func(t *testing.T) {
|
|
args := &dashboards.DeleteDashboardsInFolderRequest{OrgID: 1, FolderUIDs: []string{"uid"}}
|
|
fakeStore.On("DeleteDashboardsInFolders", mock.Anything, args).Return(nil).Once()
|
|
fakeStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
err := service.DeleteInFolders(context.Background(), 1, []string{"uid"}, nil)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Soft Delete dashboards in folder", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures(featuremgmt.FlagDashboardRestore)
|
|
fakeStore.On("SoftDeleteDashboardsInFolders", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
err := service.DeleteInFolders(context.Background(), 1, []string{"uid"}, nil)
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
}
|
|
|
|
func setupK8sDashboardTests(service *DashboardServiceImpl) (context.Context, *client.MockK8sHandler) {
|
|
mockCli := new(client.MockK8sHandler)
|
|
service.k8sclient = mockCli
|
|
service.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
|
|
|
ctx := context.Background()
|
|
userCtx := &user.SignedInUser{UserID: 1, OrgID: 1}
|
|
ctx = identity.WithRequester(ctx, userCtx)
|
|
|
|
return ctx, mockCli
|
|
}
|
|
|
|
func TestGetDashboard(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
query := &dashboards.GetDashboardQuery{
|
|
UID: "test-uid",
|
|
OrgID: 1,
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetDashboard", mock.Anything, query).Return(&dashboards.Dashboard{}, nil).Once()
|
|
dashboard, err := service.GetDashboard(context.Background(), query)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
},
|
|
"spec": map[string]any{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}
|
|
|
|
dashboardExpected := dashboards.Dashboard{
|
|
UID: "uid", // uid is the name of the k8s object
|
|
Title: "testing slugify",
|
|
Slug: "testing-slugify", // slug is taken from title
|
|
OrgID: 1, // orgID is populated from the query
|
|
Version: 1,
|
|
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "uid", "version": int64(1)}),
|
|
}
|
|
k8sCliMock.On("Get", mock.Anything, query.UID, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil).Once()
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
|
|
dashboard, err := service.GetDashboard(ctx, query)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
k8sCliMock.AssertExpectations(t)
|
|
// make sure the conversion is working
|
|
require.True(t, reflect.DeepEqual(dashboard, &dashboardExpected))
|
|
})
|
|
|
|
t.Run("Should get uid if not passed in at first", func(t *testing.T) {
|
|
query := &dashboards.GetDashboardQuery{
|
|
ID: 1,
|
|
UID: "",
|
|
OrgID: 1,
|
|
}
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
},
|
|
"spec": map[string]any{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}
|
|
|
|
dashboardExpected := dashboards.Dashboard{
|
|
UID: "uid", // uid is the name of the k8s object
|
|
Title: "testing slugify",
|
|
Slug: "testing-slugify", // slug is taken from title
|
|
OrgID: 1, // orgID is populated from the query
|
|
Version: 1,
|
|
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "uid", "version": int64(1)}),
|
|
}
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil).Once()
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
|
|
dashboard, err := service.GetDashboard(ctx, query)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
k8sCliMock.AssertExpectations(t)
|
|
k8sCliMock.AssertExpectations(t)
|
|
// make sure the conversion is working
|
|
require.True(t, reflect.DeepEqual(dashboard, &dashboardExpected))
|
|
})
|
|
|
|
t.Run("Should return error when Kubernetes client fails", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("Get", mock.Anything, query.UID, mock.Anything, mock.Anything, mock.Anything).Return(nil, assert.AnError).Once()
|
|
|
|
dashboard, err := service.GetDashboard(ctx, query)
|
|
require.Error(t, err)
|
|
require.Nil(t, dashboard)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should return dashboard not found if Kubernetes client returns nil", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("Get", mock.Anything, query.UID, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Once()
|
|
dashboard, err := service.GetDashboard(ctx, query)
|
|
require.Error(t, err)
|
|
require.Equal(t, dashboards.ErrDashboardNotFound, err)
|
|
require.Nil(t, dashboard)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetAllDashboards(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetAllDashboards", mock.Anything).Return([]*dashboards.Dashboard{}, nil).Once()
|
|
dashboard, err := service.GetAllDashboards(context.Background())
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
|
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
},
|
|
"spec": map[string]any{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}
|
|
|
|
dashboardExpected := dashboards.Dashboard{
|
|
UID: "uid", // uid is the name of the k8s object
|
|
Title: "testing slugify",
|
|
Slug: "testing-slugify", // slug is taken from title
|
|
OrgID: 1, // orgID is populated from the query
|
|
Version: 1, // default to version 1
|
|
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "uid", "version": int64(1)}),
|
|
}
|
|
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("List", mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.UnstructuredList{Items: []unstructured.Unstructured{dashboardUnstructured}}, nil).Once()
|
|
|
|
dashes, err := service.GetAllDashboards(ctx)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashes)
|
|
k8sCliMock.AssertExpectations(t)
|
|
// make sure the conversion is working
|
|
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
|
})
|
|
}
|
|
|
|
func TestGetAllDashboardsByOrgId(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetAllDashboardsByOrgId", mock.Anything, int64(1)).Return([]*dashboards.Dashboard{}, nil).Once()
|
|
dashboard, err := service.GetAllDashboardsByOrgId(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
|
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
},
|
|
"spec": map[string]any{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}
|
|
|
|
dashboardExpected := dashboards.Dashboard{
|
|
UID: "uid", // uid is the name of the k8s object
|
|
Title: "testing slugify",
|
|
Slug: "testing-slugify", // slug is taken from title
|
|
OrgID: 1, // orgID is populated from the query
|
|
Version: 1, // default to version 1
|
|
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "uid", "version": int64(1)}),
|
|
}
|
|
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("List", mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.UnstructuredList{Items: []unstructured.Unstructured{dashboardUnstructured}}, nil).Once()
|
|
|
|
dashes, err := service.GetAllDashboardsByOrgId(ctx, 1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashes)
|
|
k8sCliMock.AssertExpectations(t)
|
|
// make sure the conversion is working
|
|
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
|
})
|
|
}
|
|
|
|
func TestGetProvisionedDashboardData(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
orgService: &orgtest.FakeOrgService{
|
|
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
|
},
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetProvisionedDashboardData", mock.Anything, "test").Return([]*dashboards.DashboardProvisioning{}, nil).Once()
|
|
dashboard, err := service.GetProvisionedDashboardData(context.Background(), "test")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled and get from relevant org", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
provisioningTimestamp := int64(1234567)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{
|
|
Object: map[string]interface{}{
|
|
"apiVersion": dashboardv0alpha1.DashboardResourceInfo.GroupVersion().String(),
|
|
"kind": dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
|
|
"metadata": map[string]interface{}{
|
|
"name": "uid",
|
|
"labels": map[string]interface{}{
|
|
utils.LabelKeyDeprecatedInternalID: "1", // nolint:staticcheck
|
|
},
|
|
"annotations": map[string]interface{}{
|
|
utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck
|
|
utils.AnnoKeyManagerIdentity: "test",
|
|
utils.AnnoKeySourceChecksum: "hash",
|
|
utils.AnnoKeySourcePath: "path/to/file",
|
|
utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()),
|
|
},
|
|
},
|
|
"spec": map[string]interface{}{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
},
|
|
}, nil).Once()
|
|
repo := "test"
|
|
k8sCliMock.On("Search", mock.Anything, int64(1),
|
|
mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool {
|
|
// make sure the kind is added to the query
|
|
return req.Options.Fields[0].Values[0] == string(utils.ManagerKindClassicFP) && // nolint:staticcheck
|
|
req.Options.Fields[1].Values[0] == repo
|
|
})).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{},
|
|
Rows: []*resource.ResourceTableRow{},
|
|
},
|
|
TotalHits: 0,
|
|
}, nil).Once()
|
|
k8sCliMock.On("Search", mock.Anything, int64(2), mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool {
|
|
// make sure the kind is added to the query
|
|
return req.Options.Fields[0].Values[0] == string(utils.ManagerKindClassicFP) && // nolint:staticcheck
|
|
req.Options.Fields[1].Values[0] == repo
|
|
})).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil).Once()
|
|
dashes, err := service.GetProvisionedDashboardData(ctx, repo)
|
|
require.NoError(t, err)
|
|
require.Len(t, dashes, 1)
|
|
require.Equal(t, dashes[0], &dashboards.DashboardProvisioning{
|
|
ID: 0,
|
|
DashboardID: 1,
|
|
Name: "test",
|
|
ExternalID: "path/to/file",
|
|
CheckSum: "hash",
|
|
Updated: provisioningTimestamp,
|
|
})
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
orgService: &orgtest.FakeOrgService{
|
|
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
|
},
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, int64(1)).Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
|
dashboard, err := service.GetProvisionedDashboardDataByDashboardID(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled and get from whatever org it is in", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
provisioningTimestamp := int64(1234567)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]interface{}{
|
|
"apiVersion": dashboardv0alpha1.DashboardResourceInfo.GroupVersion().String(),
|
|
"kind": dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
|
|
"metadata": map[string]interface{}{
|
|
"name": "uid",
|
|
"labels": map[string]interface{}{
|
|
utils.LabelKeyDeprecatedInternalID: "1", // nolint:staticcheck
|
|
},
|
|
"annotations": map[string]interface{}{
|
|
utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck
|
|
utils.AnnoKeyManagerIdentity: "test",
|
|
utils.AnnoKeySourceChecksum: "hash",
|
|
utils.AnnoKeySourcePath: "path/to/file",
|
|
utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()),
|
|
},
|
|
},
|
|
"spec": map[string]interface{}{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}, nil)
|
|
k8sCliMock.On("Search", mock.Anything, int64(1), mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{},
|
|
Rows: []*resource.ResourceTableRow{},
|
|
},
|
|
TotalHits: 0,
|
|
}, nil)
|
|
k8sCliMock.On("Search", mock.Anything, int64(2), mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
dash, err := service.GetProvisionedDashboardDataByDashboardID(ctx, 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, dash, &dashboards.DashboardProvisioning{
|
|
ID: 0,
|
|
DashboardID: 1,
|
|
Name: "test",
|
|
ExternalID: "path/to/file",
|
|
CheckSum: "hash",
|
|
Updated: provisioningTimestamp,
|
|
})
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
orgService: &orgtest.FakeOrgService{
|
|
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
|
},
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetProvisionedDataByDashboardUID", mock.Anything, int64(1), "test").Return(&dashboards.DashboardProvisioning{}, nil).Once()
|
|
dashboard, err := service.GetProvisionedDashboardDataByDashboardUID(context.Background(), 1, "test")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
provisioningTimestamp := int64(1234567)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]interface{}{
|
|
"apiVersion": dashboardv0alpha1.DashboardResourceInfo.GroupVersion().String(),
|
|
"kind": dashboardv0alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
|
|
"metadata": map[string]interface{}{
|
|
"name": "uid",
|
|
"labels": map[string]interface{}{
|
|
utils.LabelKeyDeprecatedInternalID: "1", // nolint:staticcheck
|
|
},
|
|
"annotations": map[string]interface{}{
|
|
utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck
|
|
utils.AnnoKeyManagerIdentity: "test",
|
|
utils.AnnoKeySourceChecksum: "hash",
|
|
utils.AnnoKeySourcePath: "path/to/file",
|
|
utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()),
|
|
},
|
|
},
|
|
"spec": map[string]interface{}{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}, nil).Once()
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil).Once()
|
|
dash, err := service.GetProvisionedDashboardDataByDashboardUID(ctx, 1, "uid")
|
|
require.NoError(t, err)
|
|
require.Equal(t, dash, &dashboards.DashboardProvisioning{
|
|
ID: 0,
|
|
DashboardID: 1,
|
|
Name: "test",
|
|
ExternalID: "path/to/file",
|
|
CheckSum: "hash",
|
|
Updated: provisioningTimestamp,
|
|
})
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
orgService: &orgtest.FakeOrgService{
|
|
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
|
},
|
|
publicDashboardService: fakePublicDashboardService,
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("DeleteOrphanedProvisionedDashboards", mock.Anything, &dashboards.DeleteOrphanedProvisionedDashboardsCommand{
|
|
ReaderNames: []string{"test"},
|
|
}).Return(nil).Once()
|
|
err := service.DeleteOrphanedProvisionedDashboards(context.Background(), &dashboards.DeleteOrphanedProvisionedDashboardsCommand{
|
|
ReaderNames: []string{"test"},
|
|
})
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled, delete across all orgs, but only delete file based provisioned dashboards", func(t *testing.T) {
|
|
_, 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)
|
|
fakeStore.On("CleanupAfterDelete", mock.Anything, &dashboards.DeleteDashboardCommand{UID: "uid", OrgID: 1}).Return(nil).Once()
|
|
fakeStore.On("CleanupAfterDelete", mock.Anything, &dashboards.DeleteDashboardCommand{UID: "uid3", OrgID: 2}).Return(nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
"annotations": map[string]any{
|
|
utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck
|
|
utils.AnnoKeyManagerIdentity: "orphaned",
|
|
utils.AnnoKeySourceChecksum: "hash",
|
|
utils.AnnoKeySourcePath: "path/to/file",
|
|
utils.AnnoKeySourceTimestamp: "2025-01-01T00:00:00Z",
|
|
},
|
|
},
|
|
"spec": map[string]any{},
|
|
}}, nil).Once()
|
|
// should not delete this one, because it does not start with "file:"
|
|
k8sCliMock.On("Get", mock.Anything, "uid2", mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid2",
|
|
"annotations": map[string]any{
|
|
utils.AnnoKeyManagerKind: string(utils.ManagerKindPlugin),
|
|
utils.AnnoKeyManagerIdentity: "app",
|
|
},
|
|
},
|
|
"spec": map[string]any{},
|
|
}}, nil).Once()
|
|
|
|
k8sCliMock.On("Get", mock.Anything, "uid3", mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid3",
|
|
"annotations": map[string]any{
|
|
utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck
|
|
utils.AnnoKeyManagerIdentity: "orphaned",
|
|
utils.AnnoKeySourceChecksum: "hash",
|
|
utils.AnnoKeySourcePath: "path/to/file",
|
|
utils.AnnoKeySourceTimestamp: "2025-01-01T00:00:00Z",
|
|
},
|
|
},
|
|
"spec": map[string]any{},
|
|
}}, nil).Once()
|
|
k8sCliMock.On("Search", mock.Anything, int64(1), mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool {
|
|
return req.Options.Fields[0].Key == "manager.id" && req.Options.Fields[0].Values[0] == "test" && req.Options.Fields[0].Operator == "notin"
|
|
})).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil).Once()
|
|
|
|
k8sCliMock.On("Search", mock.Anything, int64(2), mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool {
|
|
return req.Options.Fields[0].Key == "manager.id" && req.Options.Fields[0].Values[0] == "test" && req.Options.Fields[0].Operator == "notin"
|
|
})).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid2",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 2"),
|
|
[]byte("folder 2"),
|
|
},
|
|
},
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid3",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 3"),
|
|
[]byte("folder 3"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 2,
|
|
}, nil).Once()
|
|
err := service.DeleteOrphanedProvisionedDashboards(context.Background(), &dashboards.DeleteOrphanedProvisionedDashboardsCommand{
|
|
ReaderNames: []string{"test"},
|
|
})
|
|
require.NoError(t, err)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestUnprovisionDashboard(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
orgService: &orgtest.FakeOrgService{
|
|
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
|
},
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("UnprovisionDashboard", mock.Anything, int64(1)).Return(nil).Once()
|
|
err := service.UnprovisionDashboard(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled - should remove annotations", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
dash := &unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
"annotations": map[string]any{
|
|
utils.AnnoKeyManagerKind: utils.ManagerKindClassicFP, // nolint:staticcheck
|
|
utils.AnnoKeyManagerIdentity: "test",
|
|
utils.AnnoKeySourceChecksum: "hash",
|
|
utils.AnnoKeySourcePath: "path/to/file",
|
|
utils.AnnoKeySourceTimestamp: "2025-01-01T00:00:00Z",
|
|
},
|
|
},
|
|
"spec": map[string]any{},
|
|
}}
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(dash, nil)
|
|
dashWithoutAnnotations := &unstructured.Unstructured{Object: map[string]any{
|
|
"apiVersion": "dashboard.grafana.app/v0alpha1",
|
|
"kind": "Dashboard",
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
"namespace": "default",
|
|
"annotations": map[string]any{},
|
|
},
|
|
"spec": map[string]any{
|
|
"uid": "uid",
|
|
"version": 1,
|
|
},
|
|
}}
|
|
// should update it to be without annotations
|
|
k8sCliMock.On("Update", mock.Anything, dashWithoutAnnotations, mock.Anything).Return(dashWithoutAnnotations, nil)
|
|
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
err := service.UnprovisionDashboard(ctx, 1)
|
|
require.NoError(t, err)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetDashboardsByPluginID(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
query := &dashboards.GetDashboardsByPluginIDQuery{
|
|
PluginID: "testing",
|
|
OrgID: 1,
|
|
}
|
|
uidUnstructured := &unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid1",
|
|
},
|
|
"spec": map[string]any{
|
|
"title": "Dashboard 1",
|
|
},
|
|
}}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetDashboardsByPluginID", mock.Anything, mock.Anything).Return([]*dashboards.Dashboard{}, nil).Once()
|
|
_, err := service.GetDashboardsByPluginID(context.Background(), query)
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(uidUnstructured, nil)
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.MatchedBy(func(req *resource.ResourceSearchRequest) bool {
|
|
return ( // gofmt comment helper
|
|
req.Options.Fields[0].Key == "manager.kind" && req.Options.Fields[0].Values[0] == string(utils.ManagerKindPlugin) &&
|
|
req.Options.Fields[1].Key == "manager.id" && req.Options.Fields[1].Values[0] == "testing")
|
|
})).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
dashes, err := service.GetDashboardsByPluginID(ctx, query)
|
|
require.NoError(t, err)
|
|
require.Len(t, dashes, 1)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestSaveProvisionedDashboard(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
folderService: &foldertest.FakeService{
|
|
ExpectedFolder: &folder.Folder{
|
|
ID: 0,
|
|
UID: "general",
|
|
},
|
|
},
|
|
log: log.NewNopLogger(),
|
|
}
|
|
|
|
origNewDashboardGuardian := guardian.New
|
|
defer func() { guardian.New = origNewDashboardGuardian }()
|
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
|
|
|
query := &dashboards.SaveDashboardDTO{
|
|
OrgID: 1,
|
|
User: &user.SignedInUser{UserID: 1},
|
|
Dashboard: &dashboards.Dashboard{
|
|
UID: "uid",
|
|
Title: "testing slugify",
|
|
Slug: "testing-slugify",
|
|
OrgID: 1,
|
|
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "uid"}),
|
|
},
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
|
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
fakeStore.On("SaveDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
|
dashboard, err := service.SaveProvisionedDashboard(context.Background(), query, &dashboards.DashboardProvisioning{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
},
|
|
"spec": map[string]any{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}
|
|
|
|
t.Run("Should use Kubernetes create if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil)
|
|
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
|
|
|
dashboard, err := service.SaveProvisionedDashboard(ctx, query, &dashboards.DashboardProvisioning{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
k8sCliMock.AssertExpectations(t)
|
|
// ensure the provisioning data is still saved to the db
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestSaveDashboard(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
folderService: &foldertest.FakeService{
|
|
ExpectedFolder: &folder.Folder{},
|
|
},
|
|
}
|
|
|
|
origNewDashboardGuardian := guardian.New
|
|
defer func() { guardian.New = origNewDashboardGuardian }()
|
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
|
|
|
query := &dashboards.SaveDashboardDTO{
|
|
OrgID: 1,
|
|
User: &user.SignedInUser{UserID: 1},
|
|
Dashboard: &dashboards.Dashboard{
|
|
UID: "uid", // uid is the name of the k8s object
|
|
Title: "testing slugify",
|
|
Slug: "testing-slugify", // slug is taken from title
|
|
OrgID: 1, // orgID is populated from the query
|
|
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "uid"}),
|
|
},
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.Anything).Return(nil, nil)
|
|
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
|
fakeStore.On("SaveDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
|
dashboard, err := service.SaveDashboard(context.Background(), query, false)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
},
|
|
"spec": map[string]any{
|
|
"test": "test",
|
|
"version": int64(1),
|
|
"title": "testing slugify",
|
|
},
|
|
}}
|
|
|
|
t.Run("Should use Kubernetes create if feature flags are enabled and dashboard doesn't exist", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
|
k8sCliMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil)
|
|
|
|
dashboard, err := service.SaveDashboard(ctx, query, false)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes update if feature flags are enabled and dashboard exists", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil)
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
|
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil)
|
|
|
|
dashboard, err := service.SaveDashboard(ctx, query, false)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, dashboard)
|
|
})
|
|
|
|
t.Run("Should return an error if uid is invalid", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
|
|
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
|
k8sCliMock.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil)
|
|
|
|
query.Dashboard.UID = "invalid/uid"
|
|
_, err := service.SaveDashboard(ctx, query, false)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestDeleteDashboard(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
publicDashboardService: fakePublicDashboardService,
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("DeleteDashboard", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
|
|
err := service.DeleteDashboard(context.Background(), 1, "uid", 1)
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
fakeStore.On("CleanupAfterDelete", mock.Anything, mock.Anything).Return(nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
|
|
err := service.DeleteDashboard(ctx, 1, "uid", 1)
|
|
require.NoError(t, err)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("If UID is not passed in, it should retrieve that first", func(t *testing.T) {
|
|
ctx, 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).Once()
|
|
fakeStore.On("CleanupAfterDelete", mock.Anything, mock.Anything).Return(nil).Once()
|
|
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
err := service.DeleteDashboard(ctx, 1, "", 1)
|
|
require.NoError(t, err)
|
|
k8sCliMock.AssertExpectations(t)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestDeleteAllDashboards(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("DeleteAllDashboards", mock.Anything, mock.Anything).Return(nil).Once()
|
|
err := service.DeleteAllDashboards(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("DeleteCollection", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
|
|
|
err := service.DeleteAllDashboards(ctx, 1)
|
|
require.NoError(t, err)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestSearchDashboards(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
fakeFolders := foldertest.NewFakeService()
|
|
fakeFolders.ExpectedFolder = &folder.Folder{
|
|
Title: "testing-folder-1",
|
|
UID: "f1",
|
|
}
|
|
fakeFolders.ExpectedFolders = []*folder.Folder{fakeFolders.ExpectedFolder}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
folderService: fakeFolders,
|
|
}
|
|
|
|
expectedResult := model.HitList{
|
|
{
|
|
UID: "uid1",
|
|
OrgID: 1,
|
|
Title: "Dashboard 1",
|
|
Type: "dash-db",
|
|
URI: "db/dashboard-1",
|
|
URL: "/d/uid1/dashboard-1",
|
|
Tags: []string{
|
|
"tag1",
|
|
"tag2",
|
|
},
|
|
FolderTitle: "testing-folder-1",
|
|
FolderUID: "f1",
|
|
},
|
|
{
|
|
UID: "uid2",
|
|
OrgID: 1,
|
|
Title: "Dashboard 2",
|
|
Type: "dash-db",
|
|
URI: "db/dashboard-2",
|
|
URL: "/d/uid2/dashboard-2",
|
|
Tags: []string{},
|
|
FolderTitle: "testing-folder-1",
|
|
FolderUID: "f1",
|
|
},
|
|
}
|
|
query := dashboards.FindPersistedDashboardsQuery{
|
|
DashboardUIDs: []string{"uid1", "uid2"},
|
|
}
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
|
|
{
|
|
UID: "uid1",
|
|
Slug: "dashboard-1",
|
|
OrgID: 1,
|
|
Title: "Dashboard 1",
|
|
Tags: []string{"tag1", "tag2"},
|
|
FolderTitle: "testing-folder-1",
|
|
FolderUID: "f1",
|
|
},
|
|
{
|
|
UID: "uid2",
|
|
Slug: "dashboard-2",
|
|
OrgID: 1,
|
|
Title: "Dashboard 2",
|
|
FolderTitle: "testing-folder-1",
|
|
FolderUID: "f1",
|
|
},
|
|
}, nil).Once()
|
|
result, err := service.SearchDashboards(context.Background(), &query)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
expectedFolders := model.HitList{
|
|
{
|
|
UID: "f1",
|
|
Title: "testing-folder-1",
|
|
},
|
|
}
|
|
fakeFolders.ExpectedHitList = expectedFolders
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "tags",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid1",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("f1"),
|
|
[]byte("[\"tag1\", \"tag2\"]"),
|
|
},
|
|
},
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid2",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 2"),
|
|
[]byte("f1"),
|
|
[]byte(""),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
|
|
result, err := service.SearchDashboards(ctx, &query)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetDashboards(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
expectedResult := []*dashboards.Dashboard{
|
|
{
|
|
UID: "uid1",
|
|
Slug: "dashboard-1",
|
|
OrgID: 1,
|
|
Title: "Dashboard 1",
|
|
Data: simplejson.NewFromAny(map[string]any{"title": "Dashboard 1", "uid": "uid1"}),
|
|
},
|
|
{
|
|
UID: "uid2",
|
|
Slug: "dashboard-2",
|
|
OrgID: 1,
|
|
Title: "Dashboard 2",
|
|
Data: simplejson.NewFromAny(map[string]any{"title": "Dashboard 2", "uid": "uid2"}),
|
|
},
|
|
}
|
|
uid1Unstructured := &unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid1",
|
|
},
|
|
"spec": map[string]any{
|
|
"title": "Dashboard 1",
|
|
},
|
|
}}
|
|
uid2Unstructured := &unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid2",
|
|
},
|
|
"spec": map[string]any{
|
|
"title": "Dashboard 2",
|
|
},
|
|
}}
|
|
queryByIDs := &dashboards.GetDashboardsQuery{
|
|
DashboardIDs: []int64{1, 2},
|
|
OrgID: 1,
|
|
}
|
|
queryByUIDs := &dashboards.GetDashboardsQuery{
|
|
DashboardUIDs: []string{"uid1", "uid2"},
|
|
OrgID: 1,
|
|
}
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
|
|
// by ids
|
|
fakeStore.On("GetDashboards", mock.Anything, queryByIDs).Return(expectedResult, nil).Once()
|
|
result, err := service.GetDashboards(context.Background(), queryByIDs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
fakeStore.AssertExpectations(t)
|
|
|
|
// by uids
|
|
fakeStore.On("GetDashboards", mock.Anything, queryByUIDs).Return(expectedResult, nil).Once()
|
|
result, err = service.GetDashboards(context.Background(), queryByUIDs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Get", mock.Anything, "uid1", mock.Anything, mock.Anything, mock.Anything).Return(uid1Unstructured, nil)
|
|
k8sCliMock.On("Get", mock.Anything, "uid2", mock.Anything, mock.Anything, mock.Anything).Return(uid2Unstructured, nil)
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{}, nil)
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid1",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte(""),
|
|
},
|
|
},
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid2",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 2"),
|
|
[]byte(""),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
|
|
// by ids
|
|
result, err := service.GetDashboards(ctx, queryByIDs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
k8sCliMock.AssertExpectations(t)
|
|
|
|
// by uids
|
|
result, err = service.GetDashboards(ctx, queryByUIDs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestGetDashboardUIDByID(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
expectedResult := &dashboards.DashboardRef{
|
|
UID: "uid1",
|
|
Slug: "dashboard-1",
|
|
}
|
|
query := &dashboards.GetDashboardRefByIDQuery{
|
|
ID: 1,
|
|
}
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetDashboardUIDByID", mock.Anything, query).Return(expectedResult, nil).Once()
|
|
|
|
result, err := service.GetDashboardUIDByID(context.Background(), query)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid1",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
result, err := service.GetDashboardUIDByID(ctx, query)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
k8sCliMock.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestUnstructuredToLegacyDashboard(t *testing.T) {
|
|
k8sCliMock := new(client.MockK8sHandler)
|
|
k8sCliMock.On("GetUserFromMeta", mock.Anything, mock.Anything).Return(&user.User{ID: 10, UID: "useruid"}, nil)
|
|
dr := &DashboardServiceImpl{
|
|
k8sclient: k8sCliMock,
|
|
}
|
|
t.Run("successfully converts unstructured to legacy dashboard", func(t *testing.T) {
|
|
uid := "36b7c825-79cc-435e-acf6-c78bd96a4510"
|
|
orgID := int64(123)
|
|
title := "Test Dashboard"
|
|
now := metav1.Now()
|
|
item := &unstructured.Unstructured{
|
|
Object: map[string]interface{}{
|
|
"spec": map[string]interface{}{
|
|
"title": title,
|
|
"version": int64(1),
|
|
},
|
|
},
|
|
}
|
|
|
|
obj, err := utils.MetaAccessor(item)
|
|
require.NoError(t, err)
|
|
obj.SetCreationTimestamp(now)
|
|
obj.SetName(uid)
|
|
obj.SetCreatedBy("user:useruid")
|
|
obj.SetUpdatedBy("user:useruid")
|
|
obj.SetDeprecatedInternalID(1) // nolint:staticcheck
|
|
result, err := dr.UnstructuredToLegacyDashboard(context.Background(), item, orgID)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
assert.Equal(t, uid, result.UID)
|
|
assert.Equal(t, title, result.Title)
|
|
assert.Equal(t, orgID, result.OrgID)
|
|
assert.Equal(t, "test-dashboard", result.Slug) // should slugify the title
|
|
assert.Equal(t, false, result.HasACL)
|
|
assert.Equal(t, false, result.IsFolder)
|
|
assert.Equal(t, int64(1), result.ID)
|
|
assert.Equal(t, now.Time.Format(time.RFC3339), result.Created.Format(time.RFC3339))
|
|
assert.Equal(t, int64(10), result.CreatedBy)
|
|
assert.Equal(t, now.Time.Format(time.RFC3339), result.Updated.Format(time.RFC3339)) // updated should default to created
|
|
assert.Equal(t, int64(10), result.UpdatedBy)
|
|
})
|
|
|
|
t.Run("returns error if spec is missing", func(t *testing.T) {
|
|
item := &unstructured.Unstructured{
|
|
Object: map[string]interface{}{},
|
|
}
|
|
_, err := (&DashboardServiceImpl{}).UnstructuredToLegacyDashboard(context.Background(), item, int64(123))
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "error parsing dashboard from k8s response", err.Error())
|
|
})
|
|
}
|
|
|
|
func TestGetDashboardTags(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
expectedResult := []*dashboards.DashboardTagCloudItem{
|
|
{
|
|
Term: "tag1",
|
|
Count: 1,
|
|
},
|
|
{
|
|
Term: "tag2",
|
|
Count: 3,
|
|
},
|
|
}
|
|
query := &dashboards.GetDashboardTagsQuery{
|
|
OrgID: 1,
|
|
}
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("GetDashboardTags", mock.Anything, query).Return(expectedResult, nil).Once()
|
|
result, err := service.GetDashboardTags(context.Background(), query)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Facet: map[string]*resource.ResourceSearchResponse_Facet{
|
|
"tags": {
|
|
Terms: []*resource.ResourceSearchResponse_TermFacet{
|
|
{
|
|
Term: "tag1",
|
|
Count: 1,
|
|
},
|
|
{
|
|
Term: "tag2",
|
|
Count: 3,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, nil)
|
|
|
|
result, err := service.GetDashboardTags(ctx, query)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expectedResult, result)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestQuotaCount(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
|
|
orgs := []*org.OrgDTO{
|
|
{
|
|
ID: 1,
|
|
},
|
|
{
|
|
ID: 2,
|
|
},
|
|
}
|
|
|
|
countOrg1 := resource.ResourceStatsResponse{
|
|
Stats: []*resource.ResourceStatsResponse_Stats{
|
|
{
|
|
Count: 1,
|
|
},
|
|
},
|
|
}
|
|
countOrg2 := resource.ResourceStatsResponse{
|
|
Stats: []*resource.ResourceStatsResponse_Stats{
|
|
{
|
|
Count: 2,
|
|
},
|
|
},
|
|
}
|
|
|
|
query := "a.ScopeParameters{
|
|
OrgID: 1,
|
|
}
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("Count", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
|
_, err := service.Count(context.Background(), query)
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
orgSvc := orgtest.FakeOrgService{ExpectedOrgs: orgs}
|
|
service.orgService = &orgSvc
|
|
k8sCliMock.On("GetStats", mock.Anything, mock.Anything).Return(&countOrg2, nil).Once()
|
|
k8sCliMock.On("GetStats", mock.Anything, mock.Anything).Return(&countOrg1, nil).Once()
|
|
|
|
result, err := service.Count(ctx, query)
|
|
require.NoError(t, err)
|
|
|
|
orgTag, err := quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.OrgScope)
|
|
require.NoError(t, err)
|
|
c, _ := result.Get(orgTag)
|
|
require.Equal(t, c, int64(1))
|
|
|
|
globalTag, err := quota.NewTag(dashboards.QuotaTargetSrv, dashboards.QuotaTarget, quota.GlobalScope)
|
|
require.NoError(t, err)
|
|
c, _ = result.Get(globalTag)
|
|
require.Equal(t, c, int64(3))
|
|
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
}
|
|
|
|
func TestCountDashboardsInOrg(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
count := resource.ResourceStatsResponse{
|
|
Stats: []*resource.ResourceStatsResponse_Stats{
|
|
{
|
|
Count: 3,
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("CountInOrg", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
|
_, err := service.CountDashboardsInOrg(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("GetStats", mock.Anything, mock.Anything).Return(&count, nil).Once()
|
|
result, err := service.CountDashboardsInOrg(ctx, 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, result, int64(3))
|
|
})
|
|
}
|
|
|
|
func TestCountInFolders(t *testing.T) {
|
|
fakeStore := dashboards.FakeDashboardStore{}
|
|
defer fakeStore.AssertExpectations(t)
|
|
service := &DashboardServiceImpl{
|
|
cfg: setting.NewCfg(),
|
|
dashboardStore: &fakeStore,
|
|
}
|
|
dashs := &resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid2",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 2"),
|
|
[]byte("folder 1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 2,
|
|
}
|
|
|
|
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
|
service.features = featuremgmt.WithFeatures()
|
|
fakeStore.On("CountDashboardsInFolders", mock.Anything, mock.Anything).Return(int64(1), nil).Once()
|
|
_, err := service.CountInFolders(context.Background(), 1, []string{"folder1"}, &user.SignedInUser{})
|
|
require.NoError(t, err)
|
|
fakeStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(dashs, nil).Once()
|
|
result, err := service.CountInFolders(ctx, 1, []string{"folder1"}, &user.SignedInUser{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, result, int64(2))
|
|
})
|
|
}
|
|
|
|
func TestSearchDashboardsThroughK8sRaw(t *testing.T) {
|
|
ctx := context.Background()
|
|
k8sCliMock := new(client.MockK8sHandler)
|
|
service := &DashboardServiceImpl{k8sclient: k8sCliMock}
|
|
query := &dashboards.FindPersistedDashboardsQuery{
|
|
OrgId: 1,
|
|
}
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
res, err := service.searchDashboardsThroughK8s(ctx, query)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []*dashboards.Dashboard{
|
|
{
|
|
UID: "uid",
|
|
OrgID: 1,
|
|
FolderUID: "folder1",
|
|
Title: "Dashboard 1",
|
|
Slug: "dashboard-1", // should be slugified
|
|
},
|
|
}, res)
|
|
assert.Equal(t, "dash-db", query.Type) // query type should be added
|
|
}
|
|
|
|
func TestSearchProvisionedDashboardsThroughK8sRaw(t *testing.T) {
|
|
ctx := context.Background()
|
|
k8sCliMock := new(client.MockK8sHandler)
|
|
service := &DashboardServiceImpl{k8sclient: k8sCliMock}
|
|
query := &dashboards.FindPersistedDashboardsQuery{
|
|
OrgId: 1,
|
|
}
|
|
provisioningTimestamp := int64(1234567)
|
|
dashboardUnstructuredProvisioned := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid",
|
|
"annotations": map[string]any{
|
|
utils.AnnoKeyManagerKind: string(utils.ManagerKindClassicFP), // nolint:staticcheck
|
|
utils.AnnoKeyManagerIdentity: "test",
|
|
utils.AnnoKeySourceChecksum: "hash",
|
|
utils.AnnoKeySourcePath: "path/to/file",
|
|
utils.AnnoKeySourceTimestamp: fmt.Sprintf("%d", time.Unix(provisioningTimestamp, 0).UnixMilli()),
|
|
},
|
|
},
|
|
"spec": map[string]any{},
|
|
}}
|
|
dashboardUnstructuredNotProvisioned := unstructured.Unstructured{Object: map[string]any{
|
|
"metadata": map[string]any{
|
|
"name": "uid2",
|
|
},
|
|
"spec": map[string]any{},
|
|
}}
|
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
|
|
Results: &resource.ResourceTable{
|
|
Columns: []*resource.ResourceTableColumnDefinition{
|
|
{
|
|
Name: "title",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
{
|
|
Name: "folder",
|
|
Type: resource.ResourceTableColumnDefinition_STRING,
|
|
},
|
|
},
|
|
Rows: []*resource.ResourceTableRow{
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 1"),
|
|
[]byte("folder1"),
|
|
},
|
|
},
|
|
{
|
|
Key: &resource.ResourceKey{
|
|
Name: "uid2",
|
|
Resource: "dashboard",
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Dashboard 2"),
|
|
[]byte("folder2"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TotalHits: 1,
|
|
}, nil)
|
|
k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructuredProvisioned, nil).Once()
|
|
k8sCliMock.On("Get", mock.Anything, "uid2", mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructuredNotProvisioned, nil).Once()
|
|
res, err := service.searchProvisionedDashboardsThroughK8s(ctx, query)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []*dashboardProvisioningWithUID{
|
|
{
|
|
DashboardUID: "uid",
|
|
DashboardProvisioning: dashboards.DashboardProvisioning{
|
|
Name: "test",
|
|
ExternalID: "path/to/file",
|
|
CheckSum: "hash",
|
|
Updated: provisioningTimestamp,
|
|
},
|
|
},
|
|
}, res) // only should return the one provisioned dashboard
|
|
assert.Equal(t, "dash-db", query.Type) // query type should be added as dashboards only
|
|
}
|
|
|
|
func TestLegacySaveCommandToUnstructured(t *testing.T) {
|
|
namespace := "test-namespace"
|
|
t.Run("successfully converts save command to unstructured", func(t *testing.T) {
|
|
cmd := &dashboards.SaveDashboardCommand{
|
|
FolderUID: "folder-uid",
|
|
Message: "saving this dashboard",
|
|
Dashboard: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "test-uid"}),
|
|
}
|
|
|
|
result, err := LegacySaveCommandToUnstructured(cmd, namespace)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
assert.Equal(t, "test-uid", result.GetName())
|
|
assert.Equal(t, "test-namespace", result.GetNamespace())
|
|
spec := result.Object["spec"].(map[string]any)
|
|
assert.Equal(t, spec["version"], 1)
|
|
assert.Equal(t, result.GetAnnotations(), map[string]string{utils.AnnoKeyFolder: "folder-uid", utils.AnnoKeyMessage: "saving this dashboard"})
|
|
})
|
|
|
|
t.Run("should increase version when called", func(t *testing.T) {
|
|
cmd := &dashboards.SaveDashboardCommand{
|
|
Dashboard: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "test-uid", "version": int64(1)}),
|
|
}
|
|
result, err := LegacySaveCommandToUnstructured(cmd, namespace)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
spec := result.Object["spec"].(map[string]any)
|
|
assert.Equal(t, spec["version"], float64(2))
|
|
// folder annotation should not be set if not inside a folder
|
|
assert.Equal(t, result.GetAnnotations(), map[string]string(nil))
|
|
})
|
|
}
|