浏览代码

provisioning: support camelcase for dashboards configs

bergquist 7 年之前
父节点
当前提交
b010e4df93

+ 5 - 1
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:

+ 4 - 1
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:

+ 36 - 4
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 {

+ 34 - 29
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(len(cfg), ShouldEqual, 2)
-
-			ds := cfg[0]
+			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]
+			validateDashboardAsConfig(cfg)
+		})
 
-			So(ds2.Name, ShouldEqual, "default")
-			So(ds2.Type, ShouldEqual, "file")
-			So(ds2.OrgId, ShouldEqual, 1)
-			So(ds2.Folder, ShouldEqual, "")
-			So(ds2.Editable, ShouldBeFalse)
+		Convey("Can read config file in version 0 format", func() {
+			cfgProvider := configReader{path: oldVersion, log: logger}
+			cfg, err := cfgProvider.readConfig()
+			So(err, ShouldBeNil)
 
-			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")
+}

+ 4 - 1
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

+ 12 - 0
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

+ 62 - 0
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)