Unistore: refactor provisioning to work with folder service (#99473)

This commit is contained in:
maicon
2025-01-29 00:19:38 -03:00
committed by GitHub
parent 6908f91428
commit 20f02ec12f
13 changed files with 180 additions and 77 deletions

View File

@ -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 {

View File

@ -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,
}

View File

@ -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

View File

@ -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

View File

@ -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{},

View File

@ -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" {

View File

@ -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)

View File

@ -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
}

View File

@ -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)

View File

@ -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"}

View File

@ -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

View File

@ -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)

View File

@ -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
}