Commit b010e4df by bergquist

provisioning: support camelcase for dashboards configs

parent 4a35244c
# # config file version
# apiVersion: 1
#providers:
# - name: 'default' # - name: 'default'
# org_id: 1 # orgId: 1
# folder: '' # folder: ''
# type: file # type: file
# options: # options:
......
...@@ -177,8 +177,11 @@ It's possible to manage dashboards in Grafana by adding one or more yaml config ...@@ -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: The dashboard provider config file looks somewhat like this:
```yaml ```yaml
apiVersion: 1
providers:
- name: 'default' - name: 'default'
org_id: 1 orgId: 1
folder: '' folder: ''
type: file type: file
options: options:
......
...@@ -14,6 +14,37 @@ type configReader struct { ...@@ -14,6 +14,37 @@ type configReader struct {
log log.Logger 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) { func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) {
var dashboards []*DashboardsAsConfig var dashboards []*DashboardsAsConfig
...@@ -35,13 +66,14 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) { ...@@ -35,13 +66,14 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) {
return nil, err return nil, err
} }
var dashCfg []*DashboardsAsConfig parsedDashboards, err := parseConfigs(yamlFile)
err = yaml.Unmarshal(yamlFile, &dashCfg)
if err != nil { if err != nil {
return nil, err
} }
dashboards = append(dashboards, dashCfg...) if len(parsedDashboards) > 0 {
dashboards = append(dashboards, parsedDashboards...)
}
} }
for i := range dashboards { for i := range dashboards {
......
...@@ -9,48 +9,33 @@ import ( ...@@ -9,48 +9,33 @@ import (
var ( var (
simpleDashboardConfig string = "./test-configs/dashboards-from-disk" simpleDashboardConfig string = "./test-configs/dashboards-from-disk"
oldVersion string = "./test-configs/version-0"
brokenConfigs string = "./test-configs/broken-configs" brokenConfigs string = "./test-configs/broken-configs"
) )
func TestDashboardsAsConfig(t *testing.T) { func TestDashboardsAsConfig(t *testing.T) {
Convey("Dashboards as configuration", t, func() { Convey("Dashboards as configuration", t, func() {
logger := log.New("test-logger")
Convey("Can read config file", func() { Convey("Can read config file version 1 format", func() {
cfgProvider := configReader{path: simpleDashboardConfig, log: logger}
cfgProvider := configReader{path: simpleDashboardConfig, log: log.New("test-logger")}
cfg, err := cfgProvider.readConfig() cfg, err := cfgProvider.readConfig()
if err != nil { So(err, ShouldBeNil)
t.Fatalf("readConfig return an error %v", err)
}
So(len(cfg), ShouldEqual, 2)
ds := cfg[0] validateDashboardAsConfig(cfg)
})
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") Convey("Can read config file in version 0 format", func() {
So(ds2.Type, ShouldEqual, "file") cfgProvider := configReader{path: oldVersion, log: logger}
So(ds2.OrgId, ShouldEqual, 1) cfg, err := cfgProvider.readConfig()
So(ds2.Folder, ShouldEqual, "") So(err, ShouldBeNil)
So(ds2.Editable, ShouldBeFalse)
So(len(ds2.Options), ShouldEqual, 1) validateDashboardAsConfig(cfg)
So(ds2.Options["path"], ShouldEqual, "/var/lib/grafana/dashboards")
}) })
Convey("Should skip invalid path", func() { 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() cfg, err := cfgProvider.readConfig()
if err != nil { if err != nil {
t.Fatalf("readConfig return an error %v", err) t.Fatalf("readConfig return an error %v", err)
...@@ -61,7 +46,7 @@ func TestDashboardsAsConfig(t *testing.T) { ...@@ -61,7 +46,7 @@ func TestDashboardsAsConfig(t *testing.T) {
Convey("Should skip broken config files", func() { 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() cfg, err := cfgProvider.readConfig()
if err != nil { if err != nil {
t.Fatalf("readConfig return an error %v", err) t.Fatalf("readConfig return an error %v", err)
...@@ -71,3 +56,23 @@ func TestDashboardsAsConfig(t *testing.T) { ...@@ -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")
}
apiVersion: 1
providers:
- name: 'general dashboards' - name: 'general dashboards'
org_id: 2 orgId: 2
folder: 'developers' folder: 'developers'
editable: true editable: true
type: file type: file
......
- 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
...@@ -10,6 +10,15 @@ import ( ...@@ -10,6 +10,15 @@ import (
) )
type DashboardsAsConfig struct { 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"` 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"`
...@@ -18,6 +27,59 @@ type DashboardsAsConfig struct { ...@@ -18,6 +27,59 @@ type DashboardsAsConfig struct {
Options map[string]interface{} `json:"options" yaml:"options"` 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) { func createDashboardJson(data *simplejson.Json, lastModified time.Time, cfg *DashboardsAsConfig, folderId int64) (*dashboards.SaveDashboardDTO, error) {
dash := &dashboards.SaveDashboardDTO{} dash := &dashboards.SaveDashboardDTO{}
dash.Dashboard = models.NewDashboardFromJson(data) dash.Dashboard = models.NewDashboardFromJson(data)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment