Provisioning: Set dashboard.folder_uid column for provisioned dashboards (#77637)
Provisiong: Store folder UID to provisioned dashboards
This commit is contained in:
@@ -140,14 +140,14 @@ 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 {
|
||||
folderID, err := fr.getOrCreateFolderID(ctx, fr.Cfg, fr.dashboardProvisioningService, fr.Cfg.Folder)
|
||||
folderID, folderUID, err := fr.getOrCreateFolder(ctx, fr.Cfg, fr.dashboardProvisioningService, fr.Cfg.Folder)
|
||||
if err != nil && !errors.Is(err, ErrFolderNameMissing) {
|
||||
return err
|
||||
}
|
||||
|
||||
// save dashboards based on json files
|
||||
for path, fileInfo := range filesFoundOnDisk {
|
||||
provisioningMetadata, err := fr.saveDashboard(ctx, path, folderID, fileInfo, dashboardRefs)
|
||||
provisioningMetadata, err := fr.saveDashboard(ctx, path, folderID, folderUID, fileInfo, dashboardRefs)
|
||||
if err != nil {
|
||||
fr.log.Error("failed to save dashboard", "file", path, "error", err)
|
||||
continue
|
||||
@@ -170,12 +170,12 @@ func (fr *FileReader) storeDashboardsInFoldersFromFileStructure(ctx context.Cont
|
||||
folderName = filepath.Base(dashboardsFolder)
|
||||
}
|
||||
|
||||
folderID, err := fr.getOrCreateFolderID(ctx, fr.Cfg, fr.dashboardProvisioningService, folderName)
|
||||
folderID, folderUID, err := fr.getOrCreateFolder(ctx, fr.Cfg, fr.dashboardProvisioningService, folderName)
|
||||
if err != nil && !errors.Is(err, ErrFolderNameMissing) {
|
||||
return fmt.Errorf("can't provision folder %q from file system structure: %w", folderName, err)
|
||||
}
|
||||
|
||||
provisioningMetadata, err := fr.saveDashboard(ctx, path, folderID, fileInfo, dashboardRefs)
|
||||
provisioningMetadata, err := fr.saveDashboard(ctx, path, folderID, folderUID, fileInfo, dashboardRefs)
|
||||
usageTracker.track(provisioningMetadata)
|
||||
if err != nil {
|
||||
fr.log.Error("failed to save dashboard", "file", path, "error", err)
|
||||
@@ -219,7 +219,7 @@ func (fr *FileReader) handleMissingDashboardFiles(ctx context.Context, provision
|
||||
}
|
||||
|
||||
// saveDashboard saves or updates the dashboard provisioning file at path.
|
||||
func (fr *FileReader) saveDashboard(ctx context.Context, path string, folderID int64, fileInfo os.FileInfo,
|
||||
func (fr *FileReader) saveDashboard(ctx context.Context, path string, folderID int64, folderUID string, fileInfo os.FileInfo,
|
||||
provisionedDashboardRefs map[string]*dashboards.DashboardProvisioning) (provisioningMetadata, error) {
|
||||
provisioningMetadata := provisioningMetadata{}
|
||||
resolvedFileInfo, err := resolveSymlink(fileInfo, path)
|
||||
@@ -229,7 +229,7 @@ func (fr *FileReader) saveDashboard(ctx context.Context, path string, folderID i
|
||||
|
||||
provisionedData, alreadyProvisioned := provisionedDashboardRefs[path]
|
||||
|
||||
jsonFile, err := fr.readDashboardFromFile(path, resolvedFileInfo.ModTime(), folderID)
|
||||
jsonFile, err := fr.readDashboardFromFile(path, resolvedFileInfo.ModTime(), folderID, folderUID)
|
||||
if err != nil {
|
||||
fr.log.Error("failed to load dashboard from ", "file", path, "error", err)
|
||||
return provisioningMetadata, nil
|
||||
@@ -245,7 +245,25 @@ func (fr *FileReader) saveDashboard(ctx context.Context, path string, folderID i
|
||||
provisioningMetadata.uid = dash.Dashboard.UID
|
||||
provisioningMetadata.identity = dashboardIdentity{title: dash.Dashboard.Title, folderID: dash.Dashboard.FolderID}
|
||||
|
||||
// fix empty folder_uid from already provisioned dashboards
|
||||
if upToDate && folderUID != "" {
|
||||
d, err := fr.dashboardStore.GetDashboard(
|
||||
ctx,
|
||||
&dashboards.GetDashboardQuery{
|
||||
OrgID: jsonFile.dashboard.OrgID,
|
||||
UID: jsonFile.dashboard.Dashboard.UID,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return provisioningMetadata, err
|
||||
}
|
||||
if d.FolderUID != folderUID {
|
||||
upToDate = false
|
||||
}
|
||||
}
|
||||
|
||||
if upToDate {
|
||||
fr.log.Debug("provisioned dashboard is up to date", "provisioner", fr.Cfg.Name, "file", path, "folderId", dash.Dashboard.FolderID, "folderUid", dash.Dashboard.FolderUID)
|
||||
return provisioningMetadata, nil
|
||||
}
|
||||
|
||||
@@ -259,7 +277,7 @@ func (fr *FileReader) saveDashboard(ctx context.Context, path string, folderID i
|
||||
}
|
||||
|
||||
if !fr.isDatabaseAccessRestricted() {
|
||||
fr.log.Debug("saving new dashboard", "provisioner", fr.Cfg.Name, "file", path, "folderId", dash.Dashboard.FolderID)
|
||||
fr.log.Debug("saving new dashboard", "provisioner", fr.Cfg.Name, "file", path, "folderId", dash.Dashboard.FolderID, "folderUid", dash.Dashboard.FolderUID)
|
||||
dp := &dashboards.DashboardProvisioning{
|
||||
ExternalID: path,
|
||||
Name: fr.Cfg.Name,
|
||||
@@ -293,9 +311,9 @@ func getProvisionedDashboardsByPath(ctx context.Context, service dashboards.Dash
|
||||
return byPath, nil
|
||||
}
|
||||
|
||||
func (fr *FileReader) getOrCreateFolderID(ctx context.Context, cfg *config, service dashboards.DashboardProvisioningService, folderName string) (int64, error) {
|
||||
func (fr *FileReader) getOrCreateFolder(ctx context.Context, cfg *config, service dashboards.DashboardProvisioningService, folderName string) (int64, string, error) {
|
||||
if folderName == "" {
|
||||
return 0, ErrFolderNameMissing
|
||||
return 0, "", ErrFolderNameMissing
|
||||
}
|
||||
|
||||
cmd := &dashboards.GetDashboardQuery{
|
||||
@@ -306,7 +324,7 @@ func (fr *FileReader) getOrCreateFolderID(ctx context.Context, cfg *config, serv
|
||||
result, err := fr.dashboardStore.GetDashboard(ctx, cmd)
|
||||
|
||||
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||
return 0, err
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
// dashboard folder not found. create one.
|
||||
@@ -318,22 +336,22 @@ func (fr *FileReader) getOrCreateFolderID(ctx context.Context, cfg *config, serv
|
||||
dash.OrgID = cfg.OrgID
|
||||
// set dashboard folderUid if given
|
||||
if cfg.FolderUID == accesscontrol.GeneralFolderUID {
|
||||
return 0, dashboards.ErrFolderInvalidUID
|
||||
return 0, "", dashboards.ErrFolderInvalidUID
|
||||
}
|
||||
dash.Dashboard.SetUID(cfg.FolderUID)
|
||||
dbDash, err := service.SaveFolderForProvisionedDashboards(ctx, dash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
return dbDash.ID, nil
|
||||
return dbDash.ID, dbDash.UID, nil
|
||||
}
|
||||
|
||||
if !result.IsFolder {
|
||||
return 0, fmt.Errorf("got invalid response. expected folder, found dashboard")
|
||||
return 0, "", fmt.Errorf("got invalid response. expected folder, found dashboard")
|
||||
}
|
||||
|
||||
return result.ID, nil
|
||||
return result.ID, result.UID, nil
|
||||
}
|
||||
|
||||
func resolveSymlink(fileinfo os.FileInfo, path string) (os.FileInfo, error) {
|
||||
@@ -387,7 +405,7 @@ type dashboardJSONFile struct {
|
||||
lastModified time.Time
|
||||
}
|
||||
|
||||
func (fr *FileReader) readDashboardFromFile(path string, lastModified time.Time, folderID int64) (*dashboardJSONFile, error) {
|
||||
func (fr *FileReader) readDashboardFromFile(path string, lastModified time.Time, folderID int64, folderUID string) (*dashboardJSONFile, error) {
|
||||
// nolint:gosec
|
||||
// We can ignore the gosec G304 warning on this one because `path` comes from the provisioning configuration file.
|
||||
reader, err := os.Open(path)
|
||||
@@ -415,7 +433,7 @@ func (fr *FileReader) readDashboardFromFile(path string, lastModified time.Time,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dash, err := createDashboardJSON(data, lastModified, fr.Cfg, folderID)
|
||||
dash, err := createDashboardJSON(data, lastModified, fr.Cfg, folderID, folderUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = r.getOrCreateFolderID(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
_, _, err = r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
require.Equal(t, err, ErrFolderNameMissing)
|
||||
})
|
||||
|
||||
@@ -384,7 +384,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = r.getOrCreateFolderID(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
_, _, err = r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@@ -404,7 +404,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = r.getOrCreateFolderID(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
_, _, err = r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg.Folder)
|
||||
require.ErrorIs(t, err, dashboards.ErrFolderInvalidUID)
|
||||
})
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ type configs struct {
|
||||
AllowUIUpdates values.BoolValue `json:"allowUiUpdates" yaml:"allowUiUpdates"`
|
||||
}
|
||||
|
||||
func createDashboardJSON(data *simplejson.Json, lastModified time.Time, cfg *config, folderID int64) (*dashboards.SaveDashboardDTO, error) {
|
||||
func createDashboardJSON(data *simplejson.Json, lastModified time.Time, cfg *config, folderID int64, folderUID string) (*dashboards.SaveDashboardDTO, error) {
|
||||
dash := &dashboards.SaveDashboardDTO{}
|
||||
dash.Dashboard = dashboards.NewDashboardFromJson(data)
|
||||
dash.UpdatedAt = lastModified
|
||||
@@ -64,6 +64,7 @@ func createDashboardJSON(data *simplejson.Json, lastModified time.Time, cfg *con
|
||||
dash.OrgID = cfg.OrgID
|
||||
dash.Dashboard.OrgID = cfg.OrgID
|
||||
dash.Dashboard.FolderID = folderID
|
||||
dash.Dashboard.FolderUID = folderUID
|
||||
|
||||
if dash.Dashboard.Title == "" {
|
||||
return nil, dashboards.ErrDashboardTitleEmpty
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
fakeService.On("SaveFolderForProvisionedDashboards", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, 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)
|
||||
folderID, err := r.getOrCreateFolderID(context.Background(), cfg, fakeService, folderName)
|
||||
folderID, _, err := r.getOrCreateFolder(context.Background(), cfg, fakeService, folderName)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity := dashboardIdentity{folderID: folderID, title: "Grafana"}
|
||||
@@ -92,7 +92,7 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
fakeStore := &fakeDashboardStore{}
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore)
|
||||
require.NoError(t, err)
|
||||
folderID, err := r.getOrCreateFolderID(context.Background(), cfg, fakeService, folderName)
|
||||
folderID, _, err := r.getOrCreateFolder(context.Background(), cfg, fakeService, folderName)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity := dashboardIdentity{folderID: folderID, title: "Grafana"}
|
||||
@@ -194,7 +194,7 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
|
||||
r, err := NewDashboardFileReader(cfg, logger, nil, fakeStore)
|
||||
require.NoError(t, err)
|
||||
folderID, err := r.getOrCreateFolderID(context.Background(), cfg, fakeService, cfg1.Folder)
|
||||
folderID, _, err := r.getOrCreateFolder(context.Background(), cfg, fakeService, cfg1.Folder)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity := dashboardIdentity{folderID: folderID, title: "Grafana"}
|
||||
@@ -211,7 +211,7 @@ func TestDuplicatesValidator(t *testing.T) {
|
||||
|
||||
r, err = NewDashboardFileReader(cfg3, logger, nil, fakeStore)
|
||||
require.NoError(t, err)
|
||||
folderID, err = r.getOrCreateFolderID(context.Background(), cfg3, fakeService, cfg3.Folder)
|
||||
folderID, _, err = r.getOrCreateFolder(context.Background(), cfg3, fakeService, cfg3.Folder)
|
||||
require.NoError(t, err)
|
||||
|
||||
identity = dashboardIdentity{folderID: folderID, title: "Grafana"}
|
||||
|
||||
Reference in New Issue
Block a user