mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
dashboards as cfg: read first cfg version
This commit is contained in:
6
conf/dashboards/dashboards.yaml
Normal file
6
conf/dashboards/dashboards.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- name: 'default'
|
||||||
|
org_id: 1
|
||||||
|
folder: ''
|
||||||
|
type: file
|
||||||
|
options:
|
||||||
|
folder: /var/lib/grafana/dashboards
|
||||||
@@ -23,6 +23,9 @@ plugins = data/plugins
|
|||||||
# Config files containing datasources that will be configured at startup
|
# Config files containing datasources that will be configured at startup
|
||||||
datasources = conf/datasources
|
datasources = conf/datasources
|
||||||
|
|
||||||
|
# Config files containing folders to read dashboards from and insert into the database.
|
||||||
|
dashboards = conf/dashboards
|
||||||
|
|
||||||
#################################### Server ##############################
|
#################################### Server ##############################
|
||||||
[server]
|
[server]
|
||||||
# Protocol (http, https, socket)
|
# Protocol (http, https, socket)
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
# Config files containing datasources that will be configured at startup
|
# Config files containing datasources that will be configured at startup
|
||||||
;datasources = conf/datasources
|
;datasources = conf/datasources
|
||||||
|
|
||||||
|
# Config files containing folders to read dashboards from and insert into the database.
|
||||||
|
;dashboards = conf/dashboards
|
||||||
|
|
||||||
#################################### Server ####################################
|
#################################### Server ####################################
|
||||||
[server]
|
[server]
|
||||||
# Protocol (http, https, socket)
|
# Protocol (http, https, socket)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (g *GrafanaServerImpl) Start() {
|
|||||||
social.NewOAuthService()
|
social.NewOAuthService()
|
||||||
plugins.Init()
|
plugins.Init()
|
||||||
|
|
||||||
if err := provisioning.StartUp(setting.DatasourcesPath); err != nil {
|
if err := provisioning.Init(g.context, setting.HomePath, setting.Cfg); err != nil {
|
||||||
logger.Error("Failed to provision Grafana from config", "error", err)
|
logger.Error("Failed to provision Grafana from config", "error", err)
|
||||||
g.Shutdown(1, "Startup failed")
|
g.Shutdown(1, "Startup failed")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ import (
|
|||||||
|
|
||||||
// Typed errors
|
// Typed errors
|
||||||
var (
|
var (
|
||||||
ErrDashboardNotFound = errors.New("Dashboard not found")
|
ErrDashboardNotFound = errors.New("Dashboard not found")
|
||||||
ErrDashboardSnapshotNotFound = errors.New("Dashboard snapshot not found")
|
ErrDashboardSnapshotNotFound = errors.New("Dashboard snapshot not found")
|
||||||
ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
|
ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
|
||||||
ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else")
|
ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else")
|
||||||
ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty")
|
ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty")
|
||||||
|
ErrDashboardContainsInvalidAlertData = errors.New("Invalid alert data. Cannot save dashboard")
|
||||||
)
|
)
|
||||||
|
|
||||||
type UpdatePluginDashboardError struct {
|
type UpdatePluginDashboardError struct {
|
||||||
@@ -139,6 +140,8 @@ type SaveDashboardCommand struct {
|
|||||||
RestoredFrom int `json:"-"`
|
RestoredFrom int `json:"-"`
|
||||||
PluginId string `json:"-"`
|
PluginId string `json:"-"`
|
||||||
|
|
||||||
|
UpdatedAt time.Time
|
||||||
|
|
||||||
Result *Dashboard
|
Result *Dashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
49
pkg/services/provisioning/dashboard/config_reader.go
Normal file
49
pkg/services/provisioning/dashboard/config_reader.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configReader struct {
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) {
|
||||||
|
files, err := ioutil.ReadDir(cr.path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var dashboards []*DashboardsAsConfig
|
||||||
|
for _, file := range files {
|
||||||
|
if !strings.HasSuffix(file.Name(), ".yaml") && !strings.HasSuffix(file.Name(), ".yml") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filename, _ := filepath.Abs(filepath.Join(cr.path, file.Name()))
|
||||||
|
yamlFile, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var datasource []*DashboardsAsConfig
|
||||||
|
err = yaml.Unmarshal(yamlFile, &datasource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboards = append(dashboards, datasource...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range dashboards {
|
||||||
|
if dashboards[i].OrgId == 0 {
|
||||||
|
dashboards[i].OrgId = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dashboards, nil
|
||||||
|
}
|
||||||
51
pkg/services/provisioning/dashboard/dashboard.go
Normal file
51
pkg/services/provisioning/dashboard/dashboard.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DashboardProvisioner struct {
|
||||||
|
cfgReader *configReader
|
||||||
|
log log.Logger
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func Provision(ctx context.Context, configDirectory string) (*DashboardProvisioner, error) {
|
||||||
|
d := &DashboardProvisioner{
|
||||||
|
cfgReader: &configReader{path: configDirectory},
|
||||||
|
log: log.New("provisioning.dashboard"),
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, d.Init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *DashboardProvisioner) Init(ctx context.Context) error {
|
||||||
|
cfgs, err := provider.cfgReader.readConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cfg := range cfgs {
|
||||||
|
if cfg.Type == "file" {
|
||||||
|
fileReader, err := NewDashboardFilereader(cfg, provider.log.New("type", cfg.Type, "name", cfg.Name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// err = fileReader.Init()
|
||||||
|
// if err != nil {
|
||||||
|
// provider.log.Error("Failed to load dashboards", "error", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
go fileReader.Listen(ctx)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("type %s is not supported", cfg.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
47
pkg/services/provisioning/dashboard/dashboard_test.go
Normal file
47
pkg/services/provisioning/dashboard/dashboard_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
simpleDashboardConfig string = "./test-configs/dashboards-from-disk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDashboardsAsConfig(t *testing.T) {
|
||||||
|
Convey("Dashboards as configuration", t, func() {
|
||||||
|
|
||||||
|
Convey("Can read config file", func() {
|
||||||
|
|
||||||
|
cfgProvifer := configReader{path: simpleDashboardConfig}
|
||||||
|
cfg, err := cfgProvifer.readConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("readConfig return an error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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(len(ds.Options), ShouldEqual, 1)
|
||||||
|
So(ds.Options["folder"], 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(len(ds2.Options), ShouldEqual, 1)
|
||||||
|
So(ds2.Options["folder"], ShouldEqual, "/var/lib/grafana/dashboards")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
212
pkg/services/provisioning/dashboard/file_reader.go
Normal file
212
pkg/services/provisioning/dashboard/file_reader.go
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileReader struct {
|
||||||
|
Cfg *DashboardsAsConfig
|
||||||
|
Path string
|
||||||
|
log log.Logger
|
||||||
|
dashboardCache *dashboardCache
|
||||||
|
}
|
||||||
|
|
||||||
|
type dashboardCache struct {
|
||||||
|
mutex *sync.Mutex
|
||||||
|
dashboards map[string]*DashboardJson
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDashboardCache() *dashboardCache {
|
||||||
|
return &dashboardCache{
|
||||||
|
dashboards: map[string]*DashboardJson{},
|
||||||
|
mutex: &sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dashboardCache) addCache(json *DashboardJson) {
|
||||||
|
dc.mutex.Lock()
|
||||||
|
defer dc.mutex.Unlock()
|
||||||
|
dc.dashboards[json.Path] = json
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dashboardCache) getCache(path string) (*DashboardJson, bool) {
|
||||||
|
dc.mutex.Lock()
|
||||||
|
defer dc.mutex.Unlock()
|
||||||
|
v, exist := dc.dashboards[path]
|
||||||
|
return v, exist
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDashboardFilereader(cfg *DashboardsAsConfig, log log.Logger) (*fileReader, error) {
|
||||||
|
path, ok := cfg.Options["folder"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Failed to load dashboards. folder param is not a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
log.Error("Cannot read directory", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &fileReader{
|
||||||
|
Cfg: cfg,
|
||||||
|
Path: path,
|
||||||
|
log: log,
|
||||||
|
dashboardCache: newDashboardCache(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *fileReader) Listen(ctx context.Context) error {
|
||||||
|
ticker := time.NewTicker(time.Second * 1)
|
||||||
|
|
||||||
|
if err := fr.walkFolder(); err != nil {
|
||||||
|
fr.log.Error("failed to search for dashboards", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
fr.walkFolder()
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *fileReader) walkFolder() error {
|
||||||
|
if _, err := os.Stat(fr.Path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Walk(fr.Path, func(path string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f.IsDir() {
|
||||||
|
if strings.HasPrefix(f.Name(), ".") {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(f.Name(), ".json") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedDashboard, exist := fr.dashboardCache.getCache(path)
|
||||||
|
if exist && cachedDashboard.ModTime == f.ModTime() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dash, err := fr.readDashboardFromFile(path)
|
||||||
|
if err != nil {
|
||||||
|
fr.log.Error("failed to load dashboard from ", "file", path, "error", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := &models.GetDashboardQuery{Slug: dash.Dashboard.Slug}
|
||||||
|
err = bus.Dispatch(cmd)
|
||||||
|
|
||||||
|
if err == models.ErrDashboardNotFound {
|
||||||
|
fr.log.Debug("saving new dashboard", "file", path)
|
||||||
|
return fr.saveDashboard(dash)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fr.log.Error("failed to query for dashboard", "slug", dash.Dashboard.Slug, "error", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Result.Updated.Unix() >= f.ModTime().Unix() {
|
||||||
|
fr.log.Debug("already using latest version", "dashboard", dash.Dashboard.Slug)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.log.Debug("no dashboard in cache. Loading dashboard from disk into database.", "file", path)
|
||||||
|
return fr.saveDashboard(dash)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *fileReader) readDashboardFromFile(path string) (*DashboardJson, error) {
|
||||||
|
reader, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
data, err := simplejson.NewFromReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, _ := os.Stat(path)
|
||||||
|
dash := &DashboardJson{}
|
||||||
|
dash.Dashboard = models.NewDashboardFromJson(data)
|
||||||
|
dash.TitleLower = strings.ToLower(dash.Dashboard.Title)
|
||||||
|
dash.Path = path
|
||||||
|
dash.ModTime = stat.ModTime()
|
||||||
|
dash.OrgId = fr.Cfg.OrgId
|
||||||
|
dash.Folder = fr.Cfg.Folder
|
||||||
|
|
||||||
|
if dash.Dashboard.Title == "" {
|
||||||
|
return nil, models.ErrDashboardTitleEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.dashboardCache.addCache(dash)
|
||||||
|
|
||||||
|
return dash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fr *fileReader) saveDashboard(dashboardJson *DashboardJson) error {
|
||||||
|
dash := dashboardJson.Dashboard
|
||||||
|
|
||||||
|
if dash.Title == "" {
|
||||||
|
return models.ErrDashboardTitleEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
validateAlertsCmd := alerting.ValidateDashboardAlertsCommand{
|
||||||
|
OrgId: dashboardJson.OrgId,
|
||||||
|
Dashboard: dash,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bus.Dispatch(&validateAlertsCmd); err != nil {
|
||||||
|
return models.ErrDashboardContainsInvalidAlertData
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
Dashboard: dash.Data,
|
||||||
|
Message: "Dashboard created from file.",
|
||||||
|
OrgId: dashboardJson.OrgId,
|
||||||
|
Overwrite: true,
|
||||||
|
UpdatedAt: dashboardJson.ModTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bus.Dispatch(&cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
alertCmd := alerting.UpdateDashboardAlertsCommand{
|
||||||
|
OrgId: dashboardJson.OrgId,
|
||||||
|
Dashboard: cmd.Result,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bus.Dispatch(&alertCmd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
88
pkg/services/provisioning/dashboard/file_reader_test.go
Normal file
88
pkg/services/provisioning/dashboard/file_reader_test.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultDashboards string = "./test-dashboards/folder-one"
|
||||||
|
brokenDashboards string = "./test-dashboards/broken-dashboards"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDashboardFileReader(t *testing.T) {
|
||||||
|
Convey("Reading dashboards from disk", t, func() {
|
||||||
|
bus.ClearBusHandlers()
|
||||||
|
|
||||||
|
bus.AddHandler("test", mockGetDashboardQuery)
|
||||||
|
bus.AddHandler("test", mockValidateDashboardAlertsCommand)
|
||||||
|
bus.AddHandler("test", mockSaveDashboardCommand)
|
||||||
|
bus.AddHandler("test", mockUpdateDashboardAlertsCommand)
|
||||||
|
logger := log.New("test.logger")
|
||||||
|
|
||||||
|
Convey("Can read default dashboard", func() {
|
||||||
|
cfg := &DashboardsAsConfig{
|
||||||
|
Name: "Default",
|
||||||
|
Type: "file",
|
||||||
|
OrgId: 1,
|
||||||
|
Folder: "",
|
||||||
|
Options: map[string]interface{}{
|
||||||
|
"folder": defaultDashboards,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
reader, err := NewDashboardFilereader(cfg, logger)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = reader.walkFolder()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Invalid configuration should return error", func() {
|
||||||
|
cfg := &DashboardsAsConfig{
|
||||||
|
Name: "Default",
|
||||||
|
Type: "file",
|
||||||
|
OrgId: 1,
|
||||||
|
Folder: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := NewDashboardFilereader(cfg, logger)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Broken dashboards should not cause error", func() {
|
||||||
|
cfg := &DashboardsAsConfig{
|
||||||
|
Name: "Default",
|
||||||
|
Type: "file",
|
||||||
|
OrgId: 1,
|
||||||
|
Folder: "",
|
||||||
|
Options: map[string]interface{}{
|
||||||
|
"folder": brokenDashboards,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := NewDashboardFilereader(cfg, logger)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockGetDashboardQuery(cmd *models.GetDashboardQuery) error {
|
||||||
|
return models.ErrDashboardNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockValidateDashboardAlertsCommand(cmd *alerting.ValidateDashboardAlertsCommand) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockSaveDashboardCommand(cmd *models.SaveDashboardCommand) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockUpdateDashboardAlertsCommand(cmd *alerting.UpdateDashboardAlertsCommand) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
- name: 'general dashboards'
|
||||||
|
org_id: 2
|
||||||
|
folder: 'developers'
|
||||||
|
type: file
|
||||||
|
options:
|
||||||
|
folder: /var/lib/grafana/dashboards
|
||||||
|
|
||||||
|
- name: 'default'
|
||||||
|
type: file
|
||||||
|
options:
|
||||||
|
folder: /var/lib/grafana/dashboards
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
[]
|
||||||
|
{
|
||||||
|
"title": "Grafana",
|
||||||
|
"tags": [],
|
||||||
|
"style": "dark",
|
||||||
|
"timezone": "browser",
|
||||||
|
"editable": true,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"title": "New row",
|
||||||
|
"height": "150px",
|
||||||
|
"collapse": false,
|
||||||
|
"editable": true,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"span": 12,
|
||||||
|
"editable": true,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<div class=\"text-center\" style=\"padding-top: 15px\">\n<img src=\"img/logo_transparent_200x.png\"> \n</div>",
|
||||||
|
"style": {},
|
||||||
|
"title": "Welcome to"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Welcome to Grafana",
|
||||||
|
"height": "210px",
|
||||||
|
"collapse": false,
|
||||||
|
"editable": true,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"span": 6,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<br/>\n\n<div class=\"row-fluid\">\n <div class=\"span6\">\n <ul>\n <li>\n <a href=\"http://grafana.org/docs#configuration\" target=\"_blank\">Configuration</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/troubleshooting\" target=\"_blank\">Troubleshooting</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/support\" target=\"_blank\">Support</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/intro\" target=\"_blank\">Getting started</a> (Must read!)\n </li>\n </ul>\n </div>\n <div class=\"span6\">\n <ul>\n <li>\n <a href=\"http://grafana.org/docs/features/graphing\" target=\"_blank\">Graphing</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/annotations\" target=\"_blank\">Annotations</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/graphite\" target=\"_blank\">Graphite</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/influxdb\" target=\"_blank\">InfluxDB</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/opentsdb\" target=\"_blank\">OpenTSDB</a>\n </li>\n </ul>\n </div>\n</div>",
|
||||||
|
"style": {},
|
||||||
|
"title": "Documentation Links"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"span": 6,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<br/>\n\n<div class=\"row-fluid\">\n <div class=\"span12\">\n <ul>\n <li>Ctrl+S saves the current dashboard</li>\n <li>Ctrl+F Opens the dashboard finder</li>\n <li>Ctrl+H Hide/show row controls</li>\n <li>Click and drag graph title to move panel</li>\n <li>Hit Escape to exit graph when in fullscreen or edit mode</li>\n <li>Click the colored icon in the legend to change series color</li>\n <li>Ctrl or Shift + Click legend name to hide other series</li>\n </ul>\n </div>\n</div>\n",
|
||||||
|
"style": {},
|
||||||
|
"title": "Tips & Shortcuts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "test",
|
||||||
|
"height": "250px",
|
||||||
|
"editable": true,
|
||||||
|
"collapse": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"span": 12,
|
||||||
|
"type": "graph",
|
||||||
|
"x-axis": true,
|
||||||
|
"y-axis": true,
|
||||||
|
"scale": 1,
|
||||||
|
"y_formats": [
|
||||||
|
"short",
|
||||||
|
"short"
|
||||||
|
],
|
||||||
|
"grid": {
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"leftMax": null,
|
||||||
|
"rightMax": null,
|
||||||
|
"leftMin": null,
|
||||||
|
"rightMin": null,
|
||||||
|
"threshold1": null,
|
||||||
|
"threshold2": null,
|
||||||
|
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||||
|
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||||
|
},
|
||||||
|
"resolution": 100,
|
||||||
|
"lines": true,
|
||||||
|
"fill": 1,
|
||||||
|
"linewidth": 2,
|
||||||
|
"dashes": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"spaceLength": 10,
|
||||||
|
"points": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"bars": false,
|
||||||
|
"stack": true,
|
||||||
|
"spyable": true,
|
||||||
|
"options": false,
|
||||||
|
"legend": {
|
||||||
|
"show": true,
|
||||||
|
"values": false,
|
||||||
|
"min": false,
|
||||||
|
"max": false,
|
||||||
|
"current": false,
|
||||||
|
"total": false,
|
||||||
|
"avg": false
|
||||||
|
},
|
||||||
|
"interactive": true,
|
||||||
|
"legend_counts": true,
|
||||||
|
"timezone": "browser",
|
||||||
|
"percentage": false,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"steppedLine": false,
|
||||||
|
"tooltip": {
|
||||||
|
"value_type": "cumulative",
|
||||||
|
"query_as_alias": true
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"target": "randomWalk('random walk')",
|
||||||
|
"function": "mean",
|
||||||
|
"column": "value"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliasColors": {},
|
||||||
|
"aliasYAxis": {},
|
||||||
|
"title": "First Graph (click title to edit)",
|
||||||
|
"datasource": "graphite",
|
||||||
|
"renderer": "flot",
|
||||||
|
"annotate": {
|
||||||
|
"enable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
{
|
||||||
|
"type": "timepicker",
|
||||||
|
"collapse": false,
|
||||||
|
"enable": true,
|
||||||
|
"status": "Stable",
|
||||||
|
"time_options": [
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"1h",
|
||||||
|
"6h",
|
||||||
|
"12h",
|
||||||
|
"24h",
|
||||||
|
"2d",
|
||||||
|
"7d",
|
||||||
|
"30d"
|
||||||
|
],
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"now": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"version": 5
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
{
|
||||||
|
"title": "Grafana",
|
||||||
|
"tags": [],
|
||||||
|
"style": "dark",
|
||||||
|
"timezone": "browser",
|
||||||
|
"editable": true,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"title": "New row",
|
||||||
|
"height": "150px",
|
||||||
|
"collapse": false,
|
||||||
|
"editable": true,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"span": 12,
|
||||||
|
"editable": true,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<div class=\"text-center\" style=\"padding-top: 15px\">\n<img src=\"img/logo_transparent_200x.png\"> \n</div>",
|
||||||
|
"style": {},
|
||||||
|
"title": "Welcome to"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Welcome to Grafana",
|
||||||
|
"height": "210px",
|
||||||
|
"collapse": false,
|
||||||
|
"editable": true,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"span": 6,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<br/>\n\n<div class=\"row-fluid\">\n <div class=\"span6\">\n <ul>\n <li>\n <a href=\"http://grafana.org/docs#configuration\" target=\"_blank\">Configuration</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/troubleshooting\" target=\"_blank\">Troubleshooting</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/support\" target=\"_blank\">Support</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/intro\" target=\"_blank\">Getting started</a> (Must read!)\n </li>\n </ul>\n </div>\n <div class=\"span6\">\n <ul>\n <li>\n <a href=\"http://grafana.org/docs/features/graphing\" target=\"_blank\">Graphing</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/annotations\" target=\"_blank\">Annotations</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/graphite\" target=\"_blank\">Graphite</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/influxdb\" target=\"_blank\">InfluxDB</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/opentsdb\" target=\"_blank\">OpenTSDB</a>\n </li>\n </ul>\n </div>\n</div>",
|
||||||
|
"style": {},
|
||||||
|
"title": "Documentation Links"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"span": 6,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<br/>\n\n<div class=\"row-fluid\">\n <div class=\"span12\">\n <ul>\n <li>Ctrl+S saves the current dashboard</li>\n <li>Ctrl+F Opens the dashboard finder</li>\n <li>Ctrl+H Hide/show row controls</li>\n <li>Click and drag graph title to move panel</li>\n <li>Hit Escape to exit graph when in fullscreen or edit mode</li>\n <li>Click the colored icon in the legend to change series color</li>\n <li>Ctrl or Shift + Click legend name to hide other series</li>\n </ul>\n </div>\n</div>\n",
|
||||||
|
"style": {},
|
||||||
|
"title": "Tips & Shortcuts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "test",
|
||||||
|
"height": "250px",
|
||||||
|
"editable": true,
|
||||||
|
"collapse": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"span": 12,
|
||||||
|
"type": "graph",
|
||||||
|
"x-axis": true,
|
||||||
|
"y-axis": true,
|
||||||
|
"scale": 1,
|
||||||
|
"y_formats": [
|
||||||
|
"short",
|
||||||
|
"short"
|
||||||
|
],
|
||||||
|
"grid": {
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"leftMax": null,
|
||||||
|
"rightMax": null,
|
||||||
|
"leftMin": null,
|
||||||
|
"rightMin": null,
|
||||||
|
"threshold1": null,
|
||||||
|
"threshold2": null,
|
||||||
|
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||||
|
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||||
|
},
|
||||||
|
"resolution": 100,
|
||||||
|
"lines": true,
|
||||||
|
"fill": 1,
|
||||||
|
"linewidth": 2,
|
||||||
|
"dashes": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"spaceLength": 10,
|
||||||
|
"points": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"bars": false,
|
||||||
|
"stack": true,
|
||||||
|
"spyable": true,
|
||||||
|
"options": false,
|
||||||
|
"legend": {
|
||||||
|
"show": true,
|
||||||
|
"values": false,
|
||||||
|
"min": false,
|
||||||
|
"max": false,
|
||||||
|
"current": false,
|
||||||
|
"total": false,
|
||||||
|
"avg": false
|
||||||
|
},
|
||||||
|
"interactive": true,
|
||||||
|
"legend_counts": true,
|
||||||
|
"timezone": "browser",
|
||||||
|
"percentage": false,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"steppedLine": false,
|
||||||
|
"tooltip": {
|
||||||
|
"value_type": "cumulative",
|
||||||
|
"query_as_alias": true
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"target": "randomWalk('random walk')",
|
||||||
|
"function": "mean",
|
||||||
|
"column": "value"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliasColors": {},
|
||||||
|
"aliasYAxis": {},
|
||||||
|
"title": "First Graph (click title to edit)",
|
||||||
|
"datasource": "graphite",
|
||||||
|
"renderer": "flot",
|
||||||
|
"annotate": {
|
||||||
|
"enable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
{
|
||||||
|
"type": "timepicker",
|
||||||
|
"collapse": false,
|
||||||
|
"enable": true,
|
||||||
|
"status": "Stable",
|
||||||
|
"time_options": [
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"1h",
|
||||||
|
"6h",
|
||||||
|
"12h",
|
||||||
|
"24h",
|
||||||
|
"2d",
|
||||||
|
"7d",
|
||||||
|
"30d"
|
||||||
|
],
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"now": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"version": 5
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
{
|
||||||
|
"title": "Grafana",
|
||||||
|
"tags": [],
|
||||||
|
"style": "dark",
|
||||||
|
"timezone": "browser",
|
||||||
|
"editable": true,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"title": "New row",
|
||||||
|
"height": "150px",
|
||||||
|
"collapse": false,
|
||||||
|
"editable": true,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"span": 12,
|
||||||
|
"editable": true,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<div class=\"text-center\" style=\"padding-top: 15px\">\n<img src=\"img/logo_transparent_200x.png\"> \n</div>",
|
||||||
|
"style": {},
|
||||||
|
"title": "Welcome to"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Welcome to Grafana",
|
||||||
|
"height": "210px",
|
||||||
|
"collapse": false,
|
||||||
|
"editable": true,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"span": 6,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<br/>\n\n<div class=\"row-fluid\">\n <div class=\"span6\">\n <ul>\n <li>\n <a href=\"http://grafana.org/docs#configuration\" target=\"_blank\">Configuration</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/troubleshooting\" target=\"_blank\">Troubleshooting</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/support\" target=\"_blank\">Support</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/intro\" target=\"_blank\">Getting started</a> (Must read!)\n </li>\n </ul>\n </div>\n <div class=\"span6\">\n <ul>\n <li>\n <a href=\"http://grafana.org/docs/features/graphing\" target=\"_blank\">Graphing</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/annotations\" target=\"_blank\">Annotations</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/graphite\" target=\"_blank\">Graphite</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/influxdb\" target=\"_blank\">InfluxDB</a>\n </li>\n <li>\n <a href=\"http://grafana.org/docs/features/opentsdb\" target=\"_blank\">OpenTSDB</a>\n </li>\n </ul>\n </div>\n</div>",
|
||||||
|
"style": {},
|
||||||
|
"title": "Documentation Links"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"span": 6,
|
||||||
|
"type": "text",
|
||||||
|
"mode": "html",
|
||||||
|
"content": "<br/>\n\n<div class=\"row-fluid\">\n <div class=\"span12\">\n <ul>\n <li>Ctrl+S saves the current dashboard</li>\n <li>Ctrl+F Opens the dashboard finder</li>\n <li>Ctrl+H Hide/show row controls</li>\n <li>Click and drag graph title to move panel</li>\n <li>Hit Escape to exit graph when in fullscreen or edit mode</li>\n <li>Click the colored icon in the legend to change series color</li>\n <li>Ctrl or Shift + Click legend name to hide other series</li>\n </ul>\n </div>\n</div>\n",
|
||||||
|
"style": {},
|
||||||
|
"title": "Tips & Shortcuts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "test",
|
||||||
|
"height": "250px",
|
||||||
|
"editable": true,
|
||||||
|
"collapse": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"span": 12,
|
||||||
|
"type": "graph",
|
||||||
|
"x-axis": true,
|
||||||
|
"y-axis": true,
|
||||||
|
"scale": 1,
|
||||||
|
"y_formats": [
|
||||||
|
"short",
|
||||||
|
"short"
|
||||||
|
],
|
||||||
|
"grid": {
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"leftMax": null,
|
||||||
|
"rightMax": null,
|
||||||
|
"leftMin": null,
|
||||||
|
"rightMin": null,
|
||||||
|
"threshold1": null,
|
||||||
|
"threshold2": null,
|
||||||
|
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||||
|
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||||
|
},
|
||||||
|
"resolution": 100,
|
||||||
|
"lines": true,
|
||||||
|
"fill": 1,
|
||||||
|
"linewidth": 2,
|
||||||
|
"dashes": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"spaceLength": 10,
|
||||||
|
"points": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"bars": false,
|
||||||
|
"stack": true,
|
||||||
|
"spyable": true,
|
||||||
|
"options": false,
|
||||||
|
"legend": {
|
||||||
|
"show": true,
|
||||||
|
"values": false,
|
||||||
|
"min": false,
|
||||||
|
"max": false,
|
||||||
|
"current": false,
|
||||||
|
"total": false,
|
||||||
|
"avg": false
|
||||||
|
},
|
||||||
|
"interactive": true,
|
||||||
|
"legend_counts": true,
|
||||||
|
"timezone": "browser",
|
||||||
|
"percentage": false,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"steppedLine": false,
|
||||||
|
"tooltip": {
|
||||||
|
"value_type": "cumulative",
|
||||||
|
"query_as_alias": true
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"target": "randomWalk('random walk')",
|
||||||
|
"function": "mean",
|
||||||
|
"column": "value"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliasColors": {},
|
||||||
|
"aliasYAxis": {},
|
||||||
|
"title": "First Graph (click title to edit)",
|
||||||
|
"datasource": "graphite",
|
||||||
|
"renderer": "flot",
|
||||||
|
"annotate": {
|
||||||
|
"enable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
{
|
||||||
|
"type": "timepicker",
|
||||||
|
"collapse": false,
|
||||||
|
"enable": true,
|
||||||
|
"status": "Stable",
|
||||||
|
"time_options": [
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"1h",
|
||||||
|
"6h",
|
||||||
|
"12h",
|
||||||
|
"24h",
|
||||||
|
"2d",
|
||||||
|
"7d",
|
||||||
|
"30d"
|
||||||
|
],
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"now": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"version": 5
|
||||||
|
}
|
||||||
|
|
||||||
34
pkg/services/provisioning/dashboard/types.go
Normal file
34
pkg/services/provisioning/dashboard/types.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DashboardsAsConfig struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"`
|
||||||
|
OrgId int64 `json:"org_id" yaml:"org_id"`
|
||||||
|
Folder string `json:"folder" yaml:"folder"`
|
||||||
|
Options map[string]interface{} `json:"options" yaml:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DashboardJson struct {
|
||||||
|
TitleLower string
|
||||||
|
Path string
|
||||||
|
OrgId int64
|
||||||
|
Folder string
|
||||||
|
ModTime time.Time
|
||||||
|
Dashboard *models.Dashboard
|
||||||
|
}
|
||||||
|
|
||||||
|
type DashboardIndex struct {
|
||||||
|
mutex *sync.Mutex
|
||||||
|
|
||||||
|
PathToDashboard map[string]*DashboardJson
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertDashboard func(cmd *models.Dashboard) error
|
||||||
|
type UpdateDashboard func(cmd *models.SaveDashboardCommand) error
|
||||||
@@ -1,14 +1,47 @@
|
|||||||
package provisioning
|
package provisioning
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
"github.com/grafana/grafana/pkg/services/provisioning/dashboard"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning/datasources"
|
"github.com/grafana/grafana/pkg/services/provisioning/datasources"
|
||||||
|
ini "gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logger log.Logger = log.New("services.provisioning")
|
logger log.Logger = log.New("services.provisioning")
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartUp(datasourcePath string) error {
|
type Provisioner struct {
|
||||||
return datasources.Provision(datasourcePath)
|
datasourcePath string
|
||||||
|
dashboardPath string
|
||||||
|
bgContext context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init(backgroundContext context.Context, homePath string, cfg *ini.File) error {
|
||||||
|
datasourcePath := makeAbsolute(cfg.Section("paths").Key("datasources").String(), homePath)
|
||||||
|
if err := datasources.Provision(datasourcePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboardPath := makeAbsolute(cfg.Section("paths").Key("dashboards").String(), homePath)
|
||||||
|
_, err := dashboard.Provision(backgroundContext, dashboardPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) Listen() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeAbsolute(path string, root string) string {
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return filepath.Join(root, path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
|||||||
} else {
|
} else {
|
||||||
dash.Version += 1
|
dash.Version += 1
|
||||||
dash.Data.Set("version", dash.Version)
|
dash.Data.Set("version", dash.Version)
|
||||||
|
|
||||||
|
if !cmd.UpdatedAt.IsZero() {
|
||||||
|
dash.Updated = cmd.UpdatedAt
|
||||||
|
}
|
||||||
|
|
||||||
affectedRows, err = sess.Id(dash.Id).Update(dash)
|
affectedRows, err = sess.Id(dash.Id).Update(dash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ var (
|
|||||||
DataPath string
|
DataPath string
|
||||||
PluginsPath string
|
PluginsPath string
|
||||||
DatasourcesPath string
|
DatasourcesPath string
|
||||||
|
DashboardsPath string
|
||||||
CustomInitPath = "conf/custom.ini"
|
CustomInitPath = "conf/custom.ini"
|
||||||
|
|
||||||
// Log settings.
|
// Log settings.
|
||||||
@@ -475,6 +476,7 @@ func NewConfigContext(args *CommandLineArgs) error {
|
|||||||
InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name")
|
InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name")
|
||||||
PluginsPath = makeAbsolute(Cfg.Section("paths").Key("plugins").String(), HomePath)
|
PluginsPath = makeAbsolute(Cfg.Section("paths").Key("plugins").String(), HomePath)
|
||||||
DatasourcesPath = makeAbsolute(Cfg.Section("paths").Key("datasources").String(), HomePath)
|
DatasourcesPath = makeAbsolute(Cfg.Section("paths").Key("datasources").String(), HomePath)
|
||||||
|
DashboardsPath = makeAbsolute(Cfg.Section("paths").Key("dashboards").String(), HomePath)
|
||||||
|
|
||||||
server := Cfg.Section("server")
|
server := Cfg.Section("server")
|
||||||
AppUrl, AppSubUrl = parseAppUrlAndSubUrl(server)
|
AppUrl, AppSubUrl = parseAppUrlAndSubUrl(server)
|
||||||
|
|||||||
Reference in New Issue
Block a user