Merge pull request #10910 from bergquist/provisioning_disable_deleting_dashboards
provisioning: adds setting to disable dashboard deletes
This commit is contained in:
@@ -184,11 +184,14 @@ providers:
|
|||||||
orgId: 1
|
orgId: 1
|
||||||
folder: ''
|
folder: ''
|
||||||
type: file
|
type: file
|
||||||
|
disableDeletion: false
|
||||||
|
editable: false
|
||||||
options:
|
options:
|
||||||
folder: /var/lib/grafana/dashboards
|
path: /var/lib/grafana/dashboards
|
||||||
```
|
```
|
||||||
|
|
||||||
When Grafana starts, it will update/insert all dashboards available in the configured folders. If you modify the file, the dashboard will also be updated.
|
When Grafana starts, it will update/insert all dashboards available in the configured folders. If you modify the file, the dashboard will also be updated.
|
||||||
|
By default Grafana will delete dashboards in the database if the file is removed. You can disable this behavior using the `disableDeletion` setting.
|
||||||
|
|
||||||
> **Note.** Provisioning allows you to overwrite existing dashboards
|
> **Note.** Provisioning allows you to overwrite existing dashboards
|
||||||
> which leads to problems if you re-use settings that are supposed to be unique.
|
> which leads to problems if you re-use settings that are supposed to be unique.
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ func validateDashboardAsConfig(cfg []*DashboardsAsConfig) {
|
|||||||
So(ds.Editable, ShouldBeTrue)
|
So(ds.Editable, ShouldBeTrue)
|
||||||
So(len(ds.Options), ShouldEqual, 1)
|
So(len(ds.Options), ShouldEqual, 1)
|
||||||
So(ds.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards")
|
So(ds.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards")
|
||||||
|
So(ds.DisableDeletion, ShouldBeTrue)
|
||||||
|
|
||||||
ds2 := cfg[1]
|
ds2 := cfg[1]
|
||||||
So(ds2.Name, ShouldEqual, "default")
|
So(ds2.Name, ShouldEqual, "default")
|
||||||
So(ds2.Type, ShouldEqual, "file")
|
So(ds2.Type, ShouldEqual, "file")
|
||||||
@@ -75,4 +77,5 @@ func validateDashboardAsConfig(cfg []*DashboardsAsConfig) {
|
|||||||
So(ds2.Editable, ShouldBeFalse)
|
So(ds2.Editable, ShouldBeFalse)
|
||||||
So(len(ds2.Options), ShouldEqual, 1)
|
So(len(ds2.Options), ShouldEqual, 1)
|
||||||
So(ds2.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards")
|
So(ds2.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards")
|
||||||
|
So(ds2.DisableDeletion, ShouldBeFalse)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,24 +105,7 @@ func (fr *fileReader) startWalkingDisk() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// find dashboards to delete since json file is missing
|
fr.deleteDashboardIfFileIsMissing(provisionedDashboardRefs, filesFoundOnDisk)
|
||||||
var dashboardToDelete []int64
|
|
||||||
for path, provisioningData := range provisionedDashboardRefs {
|
|
||||||
_, existsOnDisk := filesFoundOnDisk[path]
|
|
||||||
if !existsOnDisk {
|
|
||||||
dashboardToDelete = append(dashboardToDelete, provisioningData.DashboardId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete dashboard that are missing json file
|
|
||||||
for _, dashboardId := range dashboardToDelete {
|
|
||||||
fr.log.Debug("deleting provisioned dashboard. missing on disk", "id", dashboardId)
|
|
||||||
cmd := &models.DeleteDashboardCommand{OrgId: fr.Cfg.OrgId, Id: dashboardId}
|
|
||||||
err := bus.Dispatch(cmd)
|
|
||||||
if err != nil {
|
|
||||||
fr.log.Error("failed to delete dashboard", "id", cmd.Id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sanityChecker := newProvisioningSanityChecker(fr.Cfg.Name)
|
sanityChecker := newProvisioningSanityChecker(fr.Cfg.Name)
|
||||||
|
|
||||||
@@ -138,6 +121,29 @@ func (fr *fileReader) startWalkingDisk() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (fr *fileReader) deleteDashboardIfFileIsMissing(provisionedDashboardRefs map[string]*models.DashboardProvisioning, filesFoundOnDisk map[string]os.FileInfo) {
|
||||||
|
if fr.Cfg.DisableDeletion {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// find dashboards to delete since json file is missing
|
||||||
|
var dashboardToDelete []int64
|
||||||
|
for path, provisioningData := range provisionedDashboardRefs {
|
||||||
|
_, existsOnDisk := filesFoundOnDisk[path]
|
||||||
|
if !existsOnDisk {
|
||||||
|
dashboardToDelete = append(dashboardToDelete, provisioningData.DashboardId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete dashboard that are missing json file
|
||||||
|
for _, dashboardId := range dashboardToDelete {
|
||||||
|
fr.log.Debug("deleting provisioned dashboard. missing on disk", "id", dashboardId)
|
||||||
|
cmd := &models.DeleteDashboardCommand{OrgId: fr.Cfg.OrgId, Id: dashboardId}
|
||||||
|
err := bus.Dispatch(cmd)
|
||||||
|
if err != nil {
|
||||||
|
fr.log.Error("failed to delete dashboard", "id", cmd.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.FileInfo, provisionedDashboardRefs map[string]*models.DashboardProvisioning) (provisioningMetadata, error) {
|
func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.FileInfo, provisionedDashboardRefs map[string]*models.DashboardProvisioning) (provisioningMetadata, error) {
|
||||||
provisioningMetadata := provisioningMetadata{}
|
provisioningMetadata := provisioningMetadata{}
|
||||||
|
|||||||
+1
@@ -5,6 +5,7 @@ providers:
|
|||||||
orgId: 2
|
orgId: 2
|
||||||
folder: 'developers'
|
folder: 'developers'
|
||||||
editable: true
|
editable: true
|
||||||
|
disableDeletion: true
|
||||||
type: file
|
type: file
|
||||||
options:
|
options:
|
||||||
path: /var/lib/grafana/dashboards
|
path: /var/lib/grafana/dashboards
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
org_id: 2
|
org_id: 2
|
||||||
folder: 'developers'
|
folder: 'developers'
|
||||||
editable: true
|
editable: true
|
||||||
|
disableDeletion: true
|
||||||
type: file
|
type: file
|
||||||
options:
|
options:
|
||||||
path: /var/lib/grafana/dashboards
|
path: /var/lib/grafana/dashboards
|
||||||
|
|||||||
@@ -10,21 +10,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DashboardsAsConfig struct {
|
type DashboardsAsConfig struct {
|
||||||
Name string
|
Name string
|
||||||
Type string
|
Type string
|
||||||
OrgId int64
|
OrgId int64
|
||||||
Folder string
|
Folder string
|
||||||
Editable bool
|
Editable bool
|
||||||
Options map[string]interface{}
|
Options map[string]interface{}
|
||||||
|
DisableDeletion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type DashboardsAsConfigV0 struct {
|
type DashboardsAsConfigV0 struct {
|
||||||
Name string `json:"name" yaml:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
Type string `json:"type" yaml:"type"`
|
Type string `json:"type" yaml:"type"`
|
||||||
OrgId int64 `json:"org_id" yaml:"org_id"`
|
OrgId int64 `json:"org_id" yaml:"org_id"`
|
||||||
Folder string `json:"folder" yaml:"folder"`
|
Folder string `json:"folder" yaml:"folder"`
|
||||||
Editable bool `json:"editable" yaml:"editable"`
|
Editable bool `json:"editable" yaml:"editable"`
|
||||||
Options map[string]interface{} `json:"options" yaml:"options"`
|
Options map[string]interface{} `json:"options" yaml:"options"`
|
||||||
|
DisableDeletion bool `json:"disableDeletion" yaml:"disableDeletion"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigVersion struct {
|
type ConfigVersion struct {
|
||||||
@@ -36,12 +38,13 @@ type DashboardAsConfigV1 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DashboardProviderConfigs struct {
|
type DashboardProviderConfigs struct {
|
||||||
Name string `json:"name" yaml:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
Type string `json:"type" yaml:"type"`
|
Type string `json:"type" yaml:"type"`
|
||||||
OrgId int64 `json:"orgId" yaml:"orgId"`
|
OrgId int64 `json:"orgId" yaml:"orgId"`
|
||||||
Folder string `json:"folder" yaml:"folder"`
|
Folder string `json:"folder" yaml:"folder"`
|
||||||
Editable bool `json:"editable" yaml:"editable"`
|
Editable bool `json:"editable" yaml:"editable"`
|
||||||
Options map[string]interface{} `json:"options" yaml:"options"`
|
Options map[string]interface{} `json:"options" yaml:"options"`
|
||||||
|
DisableDeletion bool `json:"disableDeletion" yaml:"disableDeletion"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDashboardJson(data *simplejson.Json, lastModified time.Time, cfg *DashboardsAsConfig, folderId int64) (*dashboards.SaveDashboardDTO, error) {
|
func createDashboardJson(data *simplejson.Json, lastModified time.Time, cfg *DashboardsAsConfig, folderId int64) (*dashboards.SaveDashboardDTO, error) {
|
||||||
@@ -68,12 +71,13 @@ func mapV0ToDashboardAsConfig(v0 []*DashboardsAsConfigV0) []*DashboardsAsConfig
|
|||||||
|
|
||||||
for _, v := range v0 {
|
for _, v := range v0 {
|
||||||
r = append(r, &DashboardsAsConfig{
|
r = append(r, &DashboardsAsConfig{
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
Type: v.Type,
|
Type: v.Type,
|
||||||
OrgId: v.OrgId,
|
OrgId: v.OrgId,
|
||||||
Folder: v.Folder,
|
Folder: v.Folder,
|
||||||
Editable: v.Editable,
|
Editable: v.Editable,
|
||||||
Options: v.Options,
|
Options: v.Options,
|
||||||
|
DisableDeletion: v.DisableDeletion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,12 +89,13 @@ func (dc *DashboardAsConfigV1) mapToDashboardAsConfig() []*DashboardsAsConfig {
|
|||||||
|
|
||||||
for _, v := range dc.Providers {
|
for _, v := range dc.Providers {
|
||||||
r = append(r, &DashboardsAsConfig{
|
r = append(r, &DashboardsAsConfig{
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
Type: v.Type,
|
Type: v.Type,
|
||||||
OrgId: v.OrgId,
|
OrgId: v.OrgId,
|
||||||
Folder: v.Folder,
|
Folder: v.Folder,
|
||||||
Editable: v.Editable,
|
Editable: v.Editable,
|
||||||
Options: v.Options,
|
Options: v.Options,
|
||||||
|
DisableDeletion: v.DisableDeletion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user