mirror of
https://github.com/grafana/grafana.git
synced 2025-08-06 20:59:35 +08:00
Unistore: refactor provisioning to work with folder service (#99473)
This commit is contained in:
@ -71,6 +71,8 @@ var serviceIdentityPermissions = getWildcardPermissions(
|
||||
"dashboards:write",
|
||||
"dashboards:create",
|
||||
"datasources:read",
|
||||
"alert.provisioning:write",
|
||||
"alert.provisioning.secrets:read",
|
||||
)
|
||||
|
||||
func IsServiceIdentity(ctx context.Context) bool {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
@ -48,6 +49,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour
|
||||
Title: req.Query,
|
||||
Limit: req.Limit,
|
||||
// FolderUIDs: req.FolderUIDs,
|
||||
Type: searchstore.TypeDashboard,
|
||||
SignedInUser: user,
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,10 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
/* Temporarily disabled search fallback while we add functionality
|
||||
/*
|
||||
Search Fallback was returning both Folders and Dashboards which resulted
|
||||
in issues with rendering the Folder UI. Also, filters are not implemented
|
||||
yet. For those reasons, we will be disabling Search Fallback for now
|
||||
func TestSearchFallback(t *testing.T) {
|
||||
t.Run("should hit legacy search handler on mode 0", func(t *testing.T) {
|
||||
mockClient := &MockClient{}
|
||||
@ -171,7 +174,8 @@ func TestSearchFallback(t *testing.T) {
|
||||
t.Fatalf("expected Search NOT to be called, but it was")
|
||||
}
|
||||
})
|
||||
}*/
|
||||
}
|
||||
*/
|
||||
|
||||
func TestSearchHandlerFields(t *testing.T) {
|
||||
// Create a mock client
|
||||
|
@ -131,7 +131,7 @@ func (s *Server) Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.provisioningService.RunInitProvisioners(s.context)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run initializes and starts services. This will block until all services have
|
||||
|
@ -240,9 +240,9 @@ func (s *Service) getFolderByTitleFromApiServer(ctx context.Context, orgID int64
|
||||
Key: folderkey,
|
||||
Fields: []*resource.Requirement{
|
||||
{
|
||||
Key: resource.SEARCH_FIELD_TITLE,
|
||||
Key: resource.SEARCH_FIELD_TITLE_SORT,
|
||||
Operator: string(selection.In),
|
||||
Values: []string{title},
|
||||
Values: []string{strings.ToLower(title)},
|
||||
},
|
||||
},
|
||||
Labels: []*resource.Requirement{},
|
||||
|
@ -567,7 +567,7 @@ func (r resourceClientMock) Search(ctx context.Context, in *resource.ResourceSea
|
||||
}
|
||||
|
||||
if len(in.Options.Fields) > 0 &&
|
||||
in.Options.Fields[0].Key == resource.SEARCH_FIELD_TITLE &&
|
||||
in.Options.Fields[0].Key == resource.SEARCH_FIELD_TITLE_SORT &&
|
||||
in.Options.Fields[0].Operator == "in" &&
|
||||
len(in.Options.Fields[0].Values) > 0 &&
|
||||
in.Options.Fields[0].Values[0] == "foo" {
|
||||
|
@ -45,7 +45,8 @@ func (prov *defaultAlertRuleProvisioner) Provision(ctx context.Context,
|
||||
files []*AlertingFile) error {
|
||||
for _, file := range files {
|
||||
for _, group := range file.Groups {
|
||||
u := provisionerUser(group.OrgID)
|
||||
ctx, u := identity.WithServiceIdentitiy(ctx, group.OrgID)
|
||||
|
||||
folderUID, err := prov.getOrCreateFolderFullpath(ctx, group.FolderFullpath, group.OrgID)
|
||||
if err != nil {
|
||||
prov.logger.Error("failed to get or create folder", "folder", group.FolderFullpath, "org", group.OrgID, "err", err)
|
||||
@ -120,11 +121,13 @@ func (prov *defaultAlertRuleProvisioner) getOrCreateFolderFullpath(
|
||||
|
||||
func (prov *defaultAlertRuleProvisioner) getOrCreateFolderByTitle(
|
||||
ctx context.Context, folderName string, orgID int64, parentUID *string) (string, error) {
|
||||
ctx, user := identity.WithServiceIdentitiy(ctx, orgID)
|
||||
|
||||
cmd := &folder.GetFolderQuery{
|
||||
Title: &folderName,
|
||||
ParentUID: parentUID,
|
||||
OrgID: orgID,
|
||||
SignedInUser: provisionerUser(orgID),
|
||||
SignedInUser: user,
|
||||
}
|
||||
|
||||
cmdResult, err := prov.folderService.Get(ctx, cmd)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
@ -147,6 +148,8 @@ func (fr *FileReader) isDatabaseAccessRestricted() bool {
|
||||
// storeDashboardsInFolder saves dashboards from the filesystem on disk to the folder from config
|
||||
func (fr *FileReader) storeDashboardsInFolder(ctx context.Context, filesFoundOnDisk map[string]os.FileInfo,
|
||||
dashboardRefs map[string]*dashboards.DashboardProvisioning, usageTracker *usageTracker) error {
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, fr.Cfg.OrgID)
|
||||
|
||||
folderID, folderUID, err := fr.getOrCreateFolder(ctx, fr.Cfg, fr.dashboardProvisioningService, fr.Cfg.Folder)
|
||||
if err != nil && !errors.Is(err, ErrFolderNameMissing) {
|
||||
return fmt.Errorf("%w with name %q: %w", ErrGetOrCreateFolder, fr.Cfg.Folder, err)
|
||||
@ -177,6 +180,7 @@ func (fr *FileReader) storeDashboardsInFoldersFromFileStructure(ctx context.Cont
|
||||
folderName = filepath.Base(dashboardsFolder)
|
||||
}
|
||||
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, fr.Cfg.OrgID)
|
||||
folderID, folderUID, err := fr.getOrCreateFolder(ctx, fr.Cfg, fr.dashboardProvisioningService, folderName)
|
||||
if err != nil && !errors.Is(err, ErrFolderNameMissing) {
|
||||
return fmt.Errorf("%w with name %q from file system structure: %w", ErrGetOrCreateFolder, folderName, err)
|
||||
@ -342,38 +346,42 @@ func (fr *FileReader) getOrCreateFolder(ctx context.Context, cfg *config, servic
|
||||
return 0, "", ErrFolderNameMissing
|
||||
}
|
||||
|
||||
// TODO use folder service instead
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Provisioning).Inc()
|
||||
cmd := &dashboards.GetDashboardQuery{
|
||||
FolderID: util.Pointer(int64(0)), // nolint:staticcheck
|
||||
cmd := &folder.GetFolderQuery{
|
||||
OrgID: cfg.OrgID,
|
||||
SignedInUser: user,
|
||||
}
|
||||
|
||||
if cfg.FolderUID != "" {
|
||||
cmd.UID = cfg.FolderUID
|
||||
cmd.UID = &cfg.FolderUID
|
||||
} else {
|
||||
// provisioning depends on unique names
|
||||
//nolint:staticcheck
|
||||
cmd.Title = &folderName
|
||||
}
|
||||
|
||||
result, err := fr.dashboardStore.GetDashboard(ctx, cmd)
|
||||
|
||||
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||
result, err := fr.folderService.Get(ctx, cmd)
|
||||
if err != nil && !errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
// dashboard folder not found. create one.
|
||||
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||
// set dashboard folderUid if given
|
||||
if cfg.FolderUID == accesscontrol.GeneralFolderUID {
|
||||
// do not allow the creation of folder with uid "general"
|
||||
if result != nil && result.UID == accesscontrol.GeneralFolderUID {
|
||||
return 0, "", dashboards.ErrFolderInvalidUID
|
||||
}
|
||||
|
||||
// dashboard folder not found. create one.
|
||||
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||
createCmd := &folder.CreateFolderCommand{
|
||||
OrgID: cfg.OrgID,
|
||||
UID: cfg.FolderUID,
|
||||
Title: folderName,
|
||||
SignedInUser: user,
|
||||
}
|
||||
|
||||
f, err := service.SaveFolderForProvisionedDashboards(ctx, createCmd)
|
||||
@ -385,10 +393,7 @@ func (fr *FileReader) getOrCreateFolder(ctx context.Context, cfg *config, servic
|
||||
return f.ID, f.UID, nil
|
||||
}
|
||||
|
||||
if !result.IsFolder {
|
||||
return 0, "", fmt.Errorf("got invalid response. expected folder, found dashboard")
|
||||
}
|
||||
|
||||
//nolint:staticcheck
|
||||
return result.ID, result.UID, nil
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,20 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@ -28,6 +39,10 @@ const (
|
||||
configName = "default"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testsuite.Run(m)
|
||||
}
|
||||
|
||||
func TestCreatingNewDashboardFileReader(t *testing.T) {
|
||||
setup := func() *config {
|
||||
return &config{
|
||||
@ -107,6 +122,17 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
sql, cfgT := db.InitTestDBWithCfg(t)
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
fStore := folderimpl.ProvideStore(sql)
|
||||
tagService := tagimpl.ProvideService(sql)
|
||||
dashStore, err := database.ProvideDashboardStore(sql, cfgT, features, tagService)
|
||||
require.NoError(t, err)
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sql)
|
||||
folderSvc := folderimpl.ProvideService(fStore, actest.FakeAccessControl{}, bus.ProvideBus(tracing.InitializeTracerForTest()),
|
||||
dashStore, folderStore, nil, sql, featuremgmt.WithFeatures(),
|
||||
supportbundlestest.NewFakeBundleService(), nil, cfgT, nil, tracing.InitializeTracerForTest())
|
||||
|
||||
t.Run("Reading dashboards from disk", func(t *testing.T) {
|
||||
t.Run("Can read default dashboard", func(t *testing.T) {
|
||||
setup()
|
||||
@ -116,8 +142,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
fakeService.On("GetProvisionedDashboardData", mock.Anything, configName).Return(nil, nil).Once()
|
||||
fakeService.On("SaveFolderForProvisionedDashboards", mock.Anything, mock.Anything).Return(&folder.Folder{ID: 1}, nil).Once()
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{ID: 2}, nil).Times(2)
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -137,7 +162,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
inserted++
|
||||
})
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -174,7 +199,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
|
||||
fakeService.On("GetProvisionedDashboardData", mock.Anything, configName).Return(provisionedDashboard, nil).Once()
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -202,7 +227,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
fakeService.On("GetProvisionedDashboardData", mock.Anything, configName).Return(provisionedDashboard, nil).Once()
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -237,7 +262,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
|
||||
fakeService.On("GetProvisionedDashboardData", mock.Anything, configName).Return(provisionedDashboard, nil).Once()
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -265,7 +290,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
fakeService.On("GetProvisionedDashboardData", mock.Anything, configName).Return(provisionedDashboard, nil).Once()
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -280,7 +305,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
fakeService.On("GetProvisionedDashboardData", mock.Anything, configName).Return(nil, nil).Once()
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -297,7 +322,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
fakeService.On("SaveFolderForProvisionedDashboards", mock.Anything, mock.Anything).Return(&folder.Folder{}, nil).Times(2)
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Times(3)
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -314,7 +339,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
Folder: "",
|
||||
}
|
||||
|
||||
_, err := NewDashboardFileReader(cfg, logger, nil, nil, nil)
|
||||
_, err := NewDashboardFileReader(cfg, logger, nil, nil, folderSvc)
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
|
||||
@ -322,7 +347,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
setup()
|
||||
cfg.Options["path"] = brokenDashboards
|
||||
|
||||
_, err := NewDashboardFileReader(cfg, logger, nil, nil, nil)
|
||||
_, err := NewDashboardFileReader(cfg, logger, nil, nil, folderSvc)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@ -335,14 +360,14 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
fakeService.On("SaveFolderForProvisionedDashboards", mock.Anything, mock.Anything).Return(&folder.Folder{}, nil).Times(2)
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Times(2)
|
||||
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, nil)
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, folderSvc)
|
||||
reader1.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
err = reader1.walkDisk(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, nil)
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, folderSvc)
|
||||
reader2.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -362,7 +387,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
"folder": defaultDashboards,
|
||||
},
|
||||
}
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, nil, nil)
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, nil, folderSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
@ -382,10 +407,12 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
}
|
||||
fakeService.On("SaveFolderForProvisionedDashboards", mock.Anything, mock.Anything).Return(&folder.Folder{ID: 1}, nil).Once()
|
||||
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
ctx := context.Background()
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, 1)
|
||||
_, _, err = r.getOrCreateFolder(ctx, cfg, fakeService, cfg.Folder)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@ -402,10 +429,12 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
ctx := context.Background()
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, 1)
|
||||
_, _, err = r.getOrCreateFolder(ctx, cfg, fakeService, cfg.Folder)
|
||||
require.ErrorIs(t, err, dashboards.ErrFolderInvalidUID)
|
||||
})
|
||||
|
||||
@ -457,7 +486,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
|
||||
cfg.DisableDeletion = true
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
require.NoError(t, err)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
|
||||
@ -472,7 +501,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
fakeService.On("DeleteProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
reader.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -8,9 +8,19 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -31,16 +41,30 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
}
|
||||
logger := log.New("test.logger")
|
||||
|
||||
sql, cfgT := db.InitTestDBWithCfg(t)
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
fStore := folderimpl.ProvideStore(sql)
|
||||
tagService := tagimpl.ProvideService(sql)
|
||||
dashStore, err := database.ProvideDashboardStore(sql, cfgT, features, tagService)
|
||||
require.NoError(t, err)
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sql)
|
||||
folderSvc := folderimpl.ProvideService(fStore, actest.FakeAccessControl{}, bus.ProvideBus(tracing.InitializeTracerForTest()),
|
||||
dashStore, folderStore, nil, sql, featuremgmt.WithFeatures(),
|
||||
supportbundlestest.NewFakeBundleService(), nil, cfgT, nil, tracing.InitializeTracerForTest())
|
||||
|
||||
t.Run("Duplicates validator should collect info about duplicate UIDs and titles within folders", func(t *testing.T) {
|
||||
const folderName = "duplicates-validator-folder"
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, 1)
|
||||
|
||||
fakeStore := &fakeDashboardStore{}
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
require.NoError(t, err)
|
||||
fakeService.On("SaveFolderForProvisionedDashboards", mock.Anything, mock.Anything).Return(&folder.Folder{}, nil).Times(6)
|
||||
fakeService.On("GetProvisionedDashboardData", mock.Anything, mock.AnythingOfType("string")).Return([]*dashboards.DashboardProvisioning{}, nil).Times(4)
|
||||
fakeService.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Times(5)
|
||||
_, folderUID, err := r.getOrCreateFolder(context.Background(), cfg, fakeService, folderName)
|
||||
_, folderUID, err := r.getOrCreateFolder(ctx, cfg, fakeService, folderName)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity := dashboardIdentity{folderUID: folderUID, title: "Grafana"}
|
||||
@ -54,11 +78,11 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
Options: map[string]any{"path": dashboardContainingUID},
|
||||
}
|
||||
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, nil)
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, folderSvc)
|
||||
reader1.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, nil)
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, folderSvc)
|
||||
reader2.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -90,10 +114,13 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
t.Run("Duplicates validator should not collect info about duplicate UIDs and titles within folders for different orgs", func(t *testing.T) {
|
||||
const folderName = "duplicates-validator-folder"
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, 1)
|
||||
|
||||
fakeStore := &fakeDashboardStore{}
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
require.NoError(t, err)
|
||||
_, folderUID, err := r.getOrCreateFolder(context.Background(), cfg, fakeService, folderName)
|
||||
_, folderUID, err := r.getOrCreateFolder(ctx, cfg, fakeService, folderName)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity := dashboardIdentity{folderUID: folderUID, title: "Grafana"}
|
||||
@ -107,11 +134,11 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
Options: map[string]any{"path": dashboardContainingUID},
|
||||
}
|
||||
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, nil)
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, folderSvc)
|
||||
reader1.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, nil)
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, folderSvc)
|
||||
reader2.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -168,15 +195,15 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
Name: "third", Type: "file", OrgID: 2, Folder: "duplicates-validator-folder",
|
||||
Options: map[string]any{"path": twoDashboardsWithUID},
|
||||
}
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, nil)
|
||||
reader1, err := NewDashboardFileReader(cfg1, logger, nil, fakeStore, folderSvc)
|
||||
reader1.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, nil)
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil, fakeStore, folderSvc)
|
||||
reader2.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
reader3, err := NewDashboardFileReader(cfg3, logger, nil, fakeStore, nil)
|
||||
reader3, err := NewDashboardFileReader(cfg3, logger, nil, fakeStore, folderSvc)
|
||||
reader3.dashboardProvisioningService = fakeService
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -193,9 +220,12 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
|
||||
duplicates := duplicateValidator.getDuplicates()
|
||||
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, nil)
|
||||
ctx := context.Background()
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, 1)
|
||||
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore, folderSvc)
|
||||
require.NoError(t, err)
|
||||
_, folderUID, err := r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg1.Folder)
|
||||
_, folderUID, err := r.getOrCreateFolder(ctx, cfg, fakeService, cfg1.Folder)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity := dashboardIdentity{folderUID: folderUID, title: "Grafana"}
|
||||
@ -210,9 +240,9 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
sort.Strings(titleUsageReaders)
|
||||
require.Equal(t, []string{"first"}, titleUsageReaders)
|
||||
|
||||
r, err = NewDashboardFileReader(cfg3, logger, nil, fakeStore, nil)
|
||||
r, err = NewDashboardFileReader(cfg3, logger, nil, fakeStore, folderSvc)
|
||||
require.NoError(t, err)
|
||||
_, folderUID, err = r.getOrCreateFolder(context.Background(), cfg3, fakeService, cfg3.Folder)
|
||||
_, folderUID, err = r.getOrCreateFolder(ctx, cfg3, fakeService, cfg3.Folder)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity = dashboardIdentity{folderUID: folderUID, title: "Grafana"}
|
||||
|
@ -164,6 +164,7 @@ type ProvisioningServiceImpl struct {
|
||||
folderService folder.Service
|
||||
resourcePermissions accesscontrol.ReceiverPermissionsService
|
||||
tracer tracing.Tracer
|
||||
onceInitProvisioners sync.Once
|
||||
}
|
||||
|
||||
func (ps *ProvisioningServiceImpl) RunInitProvisioners(ctx context.Context) error {
|
||||
@ -189,7 +190,19 @@ func (ps *ProvisioningServiceImpl) RunInitProvisioners(ctx context.Context) erro
|
||||
}
|
||||
|
||||
func (ps *ProvisioningServiceImpl) Run(ctx context.Context) error {
|
||||
err := ps.ProvisionDashboards(ctx)
|
||||
var err error
|
||||
|
||||
// run Init Provisioners only once
|
||||
ps.onceInitProvisioners.Do(func() {
|
||||
err = ps.RunInitProvisioners(ctx)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// error already logged
|
||||
return err
|
||||
}
|
||||
|
||||
err = ps.ProvisionDashboards(ctx)
|
||||
if err != nil {
|
||||
ps.log.Error("Failed to provision dashboard", "error", err)
|
||||
// Consider the allow list of errors for which running the provisioning service should not
|
||||
|
@ -13,7 +13,11 @@ import (
|
||||
dashboardstore "github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
prov_alerting "github.com/grafana/grafana/pkg/services/provisioning/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/utils"
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
)
|
||||
@ -159,10 +163,17 @@ func setup(t *testing.T) *serviceTestStruct {
|
||||
serviceTest.dashboardProvisionerInstantiations++
|
||||
return serviceTest.mock, nil
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
func(context.Context, string, datasources.BaseDataSourceService, datasources.CorrelationsStore, org.Service) error {
|
||||
return nil
|
||||
},
|
||||
func(context.Context, string, pluginstore.Store, pluginsettings.Service, org.Service) error {
|
||||
return nil
|
||||
},
|
||||
searchStub,
|
||||
)
|
||||
service.provisionAlerting = func(context.Context, prov_alerting.ProvisionerConfig) error {
|
||||
return nil
|
||||
}
|
||||
serviceTest.service = service
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -4,17 +4,21 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// Search Fallback was returning both Folders and Dashboards which resulted
|
||||
// in issues with rendering the Folder UI. Also, filters are not implemented
|
||||
// yet. For those reasons, we will be disabling Search Fallback for now
|
||||
func NewSearchClient(cfg *setting.Cfg, unifiedStorageConfigKey string, unifiedClient ResourceIndexClient, legacyClient ResourceIndexClient) ResourceIndexClient {
|
||||
/*config, ok := cfg.UnifiedStorage[unifiedStorageConfigKey]
|
||||
if !ok {
|
||||
return legacyClient
|
||||
}
|
||||
// config, ok := cfg.UnifiedStorage[unifiedStorageConfigKey]
|
||||
// if !ok {
|
||||
// return legacyClient
|
||||
// }
|
||||
|
||||
// switch config.DualWriterMode {
|
||||
// case rest.Mode0, rest.Mode1, rest.Mode2:
|
||||
// return legacyClient
|
||||
// default:
|
||||
// return unifiedClient
|
||||
// }
|
||||
|
||||
switch config.DualWriterMode {
|
||||
case rest.Mode0, rest.Mode1, rest.Mode2:
|
||||
return legacyClient
|
||||
default:
|
||||
return unifiedClient
|
||||
}*/
|
||||
return unifiedClient
|
||||
}
|
||||
|
Reference in New Issue
Block a user