From b010e4df9388a38b25d6ec8c64cd480526524702 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 13 Feb 2018 14:14:10 +0100 Subject: [PATCH] provisioning: support camelcase for dashboards configs --- conf/provisioning/dashboards/sample.yaml | 6 +- docs/sources/administration/provisioning.md | 5 +- .../provisioning/dashboards/config_reader.go | 40 ++++++++++-- .../dashboards/config_reader_test.go | 63 ++++++++++--------- .../dashboards-from-disk/dev-dashboards.yaml | 5 +- .../test-configs/version-0/version-0.yaml | 12 ++++ pkg/services/provisioning/dashboards/types.go | 62 ++++++++++++++++++ 7 files changed, 157 insertions(+), 36 deletions(-) create mode 100644 pkg/services/provisioning/dashboards/test-configs/version-0/version-0.yaml diff --git a/conf/provisioning/dashboards/sample.yaml b/conf/provisioning/dashboards/sample.yaml index f0dcca9b47a..2eebdee9ed5 100644 --- a/conf/provisioning/dashboards/sample.yaml +++ b/conf/provisioning/dashboards/sample.yaml @@ -1,5 +1,9 @@ +# # config file version +# apiVersion: 1 + +#providers: # - name: 'default' -# org_id: 1 +# orgId: 1 # folder: '' # type: file # options: diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md index e783798b97d..924a8245509 100644 --- a/docs/sources/administration/provisioning.md +++ b/docs/sources/administration/provisioning.md @@ -177,8 +177,11 @@ It's possible to manage dashboards in Grafana by adding one or more yaml config The dashboard provider config file looks somewhat like this: ```yaml +apiVersion: 1 + +providers: - name: 'default' - org_id: 1 + orgId: 1 folder: '' type: file options: diff --git a/pkg/services/provisioning/dashboards/config_reader.go b/pkg/services/provisioning/dashboards/config_reader.go index ab9e85f4d38..ee3cf2085b2 100644 --- a/pkg/services/provisioning/dashboards/config_reader.go +++ b/pkg/services/provisioning/dashboards/config_reader.go @@ -14,6 +14,37 @@ type configReader struct { log log.Logger } +func parseConfigs(yamlFile []byte) ([]*DashboardsAsConfig, error) { + apiVersion := &ConfigVersion{ApiVersion: 0} + yaml.Unmarshal(yamlFile, &apiVersion) + + if apiVersion.ApiVersion > 0 { + + v1 := &DashboardAsConfigV1{} + err := yaml.Unmarshal(yamlFile, &v1) + if err != nil { + return nil, err + } + + if v1 != nil { + return v1.mapToDashboardAsConfig(), nil + } + + } else { + var v0 []*DashboardsAsConfigV0 + err := yaml.Unmarshal(yamlFile, &v0) + if err != nil { + return nil, err + } + + if v0 != nil { + return convertv0ToDashboardAsConfig(v0), nil + } + } + + return []*DashboardsAsConfig{}, nil +} + func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) { var dashboards []*DashboardsAsConfig @@ -35,13 +66,14 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) { return nil, err } - var dashCfg []*DashboardsAsConfig - err = yaml.Unmarshal(yamlFile, &dashCfg) + parsedDashboards, err := parseConfigs(yamlFile) if err != nil { - return nil, err + } - dashboards = append(dashboards, dashCfg...) + if len(parsedDashboards) > 0 { + dashboards = append(dashboards, parsedDashboards...) + } } for i := range dashboards { diff --git a/pkg/services/provisioning/dashboards/config_reader_test.go b/pkg/services/provisioning/dashboards/config_reader_test.go index bb960a72094..95f9b3561ea 100644 --- a/pkg/services/provisioning/dashboards/config_reader_test.go +++ b/pkg/services/provisioning/dashboards/config_reader_test.go @@ -9,48 +9,33 @@ import ( var ( simpleDashboardConfig string = "./test-configs/dashboards-from-disk" + oldVersion string = "./test-configs/version-0" brokenConfigs string = "./test-configs/broken-configs" ) func TestDashboardsAsConfig(t *testing.T) { Convey("Dashboards as configuration", t, func() { + logger := log.New("test-logger") - Convey("Can read config file", func() { - - cfgProvider := configReader{path: simpleDashboardConfig, log: log.New("test-logger")} + Convey("Can read config file version 1 format", func() { + cfgProvider := configReader{path: simpleDashboardConfig, log: logger} cfg, err := cfgProvider.readConfig() - if err != nil { - t.Fatalf("readConfig return an error %v", err) - } + So(err, ShouldBeNil) - So(len(cfg), ShouldEqual, 2) + validateDashboardAsConfig(cfg) + }) - ds := cfg[0] + Convey("Can read config file in version 0 format", func() { + cfgProvider := configReader{path: oldVersion, log: logger} + cfg, err := cfgProvider.readConfig() + So(err, ShouldBeNil) - So(ds.Name, ShouldEqual, "general dashboards") - So(ds.Type, ShouldEqual, "file") - So(ds.OrgId, ShouldEqual, 2) - So(ds.Folder, ShouldEqual, "developers") - So(ds.Editable, ShouldBeTrue) - - So(len(ds.Options), ShouldEqual, 1) - So(ds.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards") - - ds2 := cfg[1] - - So(ds2.Name, ShouldEqual, "default") - So(ds2.Type, ShouldEqual, "file") - So(ds2.OrgId, ShouldEqual, 1) - So(ds2.Folder, ShouldEqual, "") - So(ds2.Editable, ShouldBeFalse) - - So(len(ds2.Options), ShouldEqual, 1) - So(ds2.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards") + validateDashboardAsConfig(cfg) }) Convey("Should skip invalid path", func() { - cfgProvider := configReader{path: "/invalid-directory", log: log.New("test-logger")} + cfgProvider := configReader{path: "/invalid-directory", log: logger} cfg, err := cfgProvider.readConfig() if err != nil { t.Fatalf("readConfig return an error %v", err) @@ -61,7 +46,7 @@ func TestDashboardsAsConfig(t *testing.T) { Convey("Should skip broken config files", func() { - cfgProvider := configReader{path: brokenConfigs, log: log.New("test-logger")} + cfgProvider := configReader{path: brokenConfigs, log: logger} cfg, err := cfgProvider.readConfig() if err != nil { t.Fatalf("readConfig return an error %v", err) @@ -71,3 +56,23 @@ func TestDashboardsAsConfig(t *testing.T) { }) }) } +func validateDashboardAsConfig(cfg []*DashboardsAsConfig) { + So(len(cfg), ShouldEqual, 2) + + ds := cfg[0] + So(ds.Name, ShouldEqual, "general dashboards") + So(ds.Type, ShouldEqual, "file") + So(ds.OrgId, ShouldEqual, 2) + So(ds.Folder, ShouldEqual, "developers") + So(ds.Editable, ShouldBeTrue) + So(len(ds.Options), ShouldEqual, 1) + So(ds.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards") + ds2 := cfg[1] + So(ds2.Name, ShouldEqual, "default") + So(ds2.Type, ShouldEqual, "file") + So(ds2.OrgId, ShouldEqual, 1) + So(ds2.Folder, ShouldEqual, "") + So(ds2.Editable, ShouldBeFalse) + So(len(ds2.Options), ShouldEqual, 1) + So(ds2.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards") +} diff --git a/pkg/services/provisioning/dashboards/test-configs/dashboards-from-disk/dev-dashboards.yaml b/pkg/services/provisioning/dashboards/test-configs/dashboards-from-disk/dev-dashboards.yaml index df0e6ff3044..b55fd303a86 100644 --- a/pkg/services/provisioning/dashboards/test-configs/dashboards-from-disk/dev-dashboards.yaml +++ b/pkg/services/provisioning/dashboards/test-configs/dashboards-from-disk/dev-dashboards.yaml @@ -1,5 +1,8 @@ +apiVersion: 1 + +providers: - name: 'general dashboards' - org_id: 2 + orgId: 2 folder: 'developers' editable: true type: file diff --git a/pkg/services/provisioning/dashboards/test-configs/version-0/version-0.yaml b/pkg/services/provisioning/dashboards/test-configs/version-0/version-0.yaml new file mode 100644 index 00000000000..df0e6ff3044 --- /dev/null +++ b/pkg/services/provisioning/dashboards/test-configs/version-0/version-0.yaml @@ -0,0 +1,12 @@ +- name: 'general dashboards' + org_id: 2 + folder: 'developers' + editable: true + type: file + options: + path: /var/lib/grafana/dashboards + +- name: 'default' + type: file + options: + path: /var/lib/grafana/dashboards diff --git a/pkg/services/provisioning/dashboards/types.go b/pkg/services/provisioning/dashboards/types.go index 91379b33148..160188f81db 100644 --- a/pkg/services/provisioning/dashboards/types.go +++ b/pkg/services/provisioning/dashboards/types.go @@ -10,6 +10,15 @@ import ( ) type DashboardsAsConfig struct { + Name string + Type string + OrgId int64 + Folder string + Editable bool + Options map[string]interface{} +} + +type DashboardsAsConfigV0 struct { Name string `json:"name" yaml:"name"` Type string `json:"type" yaml:"type"` OrgId int64 `json:"org_id" yaml:"org_id"` @@ -18,6 +27,59 @@ type DashboardsAsConfig struct { Options map[string]interface{} `json:"options" yaml:"options"` } +func convertv0ToDashboardAsConfig(v0 []*DashboardsAsConfigV0) []*DashboardsAsConfig { + var r []*DashboardsAsConfig + + for _, v := range v0 { + r = append(r, &DashboardsAsConfig{ + Name: v.Name, + Type: v.Type, + OrgId: v.OrgId, + Folder: v.Folder, + Editable: v.Editable, + Options: v.Options, + }) + } + + return r +} + +type ConfigVersion struct { + ApiVersion int64 `json:"apiVersion" yaml:"apiVersion"` +} + +type DashboardAsConfigV1 struct { + ApiVersion int64 `json:"apiVersion" yaml:"apiVersion"` + + Providers []*DashboardSource `json:"providers" yaml:"providers"` +} + +func (dc *DashboardAsConfigV1) mapToDashboardAsConfig() []*DashboardsAsConfig { + var r []*DashboardsAsConfig + + for _, v := range dc.Providers { + r = append(r, &DashboardsAsConfig{ + Name: v.Name, + Type: v.Type, + OrgId: v.OrgId, + Folder: v.Folder, + Editable: v.Editable, + Options: v.Options, + }) + } + + return r +} + +type DashboardSource struct { + Name string `json:"name" yaml:"name"` + Type string `json:"type" yaml:"type"` + OrgId int64 `json:"orgId" yaml:"orgId"` + Folder string `json:"folder" yaml:"folder"` + Editable bool `json:"editable" yaml:"editable"` + Options map[string]interface{} `json:"options" yaml:"options"` +} + func createDashboardJson(data *simplejson.Json, lastModified time.Time, cfg *DashboardsAsConfig, folderId int64) (*dashboards.SaveDashboardDTO, error) { dash := &dashboards.SaveDashboardDTO{} dash.Dashboard = models.NewDashboardFromJson(data)