provisioning: avoid caching and use updated field from db
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
package models
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package dashboards
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
gocache "github.com/patrickmn/go-cache"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dashboardCache struct {
|
|
||||||
internalCache *gocache.Cache
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDashboardCache() *dashboardCache {
|
|
||||||
return &dashboardCache{internalCache: gocache.New(5*time.Minute, 30*time.Minute)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fr *dashboardCache) addDashboardCache(key string, json *dashboards.SaveDashboardDTO) {
|
|
||||||
fr.internalCache.Add(key, json, time.Minute*10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fr *dashboardCache) deleteDashboard(key string) {
|
|
||||||
fr.internalCache.Delete(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fr *dashboardCache) getDashboard(key string) (*dashboards.SaveDashboardDTO, bool) {
|
|
||||||
obj, exist := fr.internalCache.Get(key)
|
|
||||||
if !exist {
|
|
||||||
return nil, exist
|
|
||||||
}
|
|
||||||
|
|
||||||
dash, ok := obj.(*dashboards.SaveDashboardDTO)
|
|
||||||
if !ok {
|
|
||||||
return nil, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
return dash, ok
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,6 @@ type fileReader struct {
|
|||||||
Path string
|
Path string
|
||||||
log log.Logger
|
log log.Logger
|
||||||
dashboardRepo dashboards.Repository
|
dashboardRepo dashboards.Repository
|
||||||
cache *dashboardCache
|
|
||||||
createWalk func(filesOnDisk map[string]os.FileInfo) filepath.WalkFunc
|
createWalk func(filesOnDisk map[string]os.FileInfo) filepath.WalkFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +53,6 @@ func NewDashboardFileReader(cfg *DashboardsAsConfig, log log.Logger) (*fileReade
|
|||||||
Path: path,
|
Path: path,
|
||||||
log: log,
|
log: log,
|
||||||
dashboardRepo: dashboards.GetRepository(),
|
dashboardRepo: dashboards.GetRepository(),
|
||||||
cache: NewDashboardCache(),
|
|
||||||
createWalk: createWalkFn,
|
createWalk: createWalkFn,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -104,8 +102,10 @@ func (fr *fileReader) startWalkingDisk() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filesFoundOnDisk := map[string]os.FileInfo{}
|
filesFoundOnDisk := map[string]os.FileInfo{}
|
||||||
|
|
||||||
err = filepath.Walk(fr.Path, fr.createWalk(filesFoundOnDisk))
|
err = filepath.Walk(fr.Path, fr.createWalk(filesFoundOnDisk))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// find dashboards to delete since json file is missing
|
// find dashboards to delete since json file is missing
|
||||||
var dashboardToDelete []int64
|
var dashboardToDelete []int64
|
||||||
@@ -113,7 +113,6 @@ func (fr *fileReader) startWalkingDisk() error {
|
|||||||
_, existsInDatabase := filesFoundOnDisk[path]
|
_, existsInDatabase := filesFoundOnDisk[path]
|
||||||
if !existsInDatabase {
|
if !existsInDatabase {
|
||||||
dashboardToDelete = append(dashboardToDelete, provisioningData.DashboardId)
|
dashboardToDelete = append(dashboardToDelete, provisioningData.DashboardId)
|
||||||
fr.cache.deleteDashboard(path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +128,9 @@ func (fr *fileReader) startWalkingDisk() error {
|
|||||||
|
|
||||||
// insert/update dashboards based on json files
|
// insert/update dashboards based on json files
|
||||||
for path, fileInfo := range filesFoundOnDisk {
|
for path, fileInfo := range filesFoundOnDisk {
|
||||||
err = fr.upsertDashboard(path, folderId, fileInfo, provisionedDashboardRefs)
|
err = fr.saveDashboard(path, folderId, fileInfo, provisionedDashboardRefs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fr.log.Error("Failed to save dashboard", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,62 +138,31 @@ func (fr *fileReader) startWalkingDisk() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fr *fileReader) upsertDashboard(path string, folderId int64, fileInfo os.FileInfo, provisionedDashboardRefs map[string]*models.DashboardProvisioning) error {
|
func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.FileInfo, provisionedDashboardRefs map[string]*models.DashboardProvisioning) error {
|
||||||
resolvedFileInfo, err := resolveSymlink(fileInfo, path)
|
resolvedFileInfo, err := resolveSymlink(fileInfo, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedDashboard, exist := fr.cache.getDashboard(path)
|
provisionedData, allReadyProvisioned := provisionedDashboardRefs[path]
|
||||||
if exist && cachedDashboard.UpdatedAt == resolvedFileInfo.ModTime() {
|
if allReadyProvisioned && provisionedData.Updated.Unix() == resolvedFileInfo.ModTime().Unix() {
|
||||||
return nil
|
return nil // dashboard is already in sync with the database
|
||||||
}
|
}
|
||||||
|
|
||||||
dash, err := fr.readDashboardFromFile(path, folderId)
|
dash, err := fr.readDashboardFromFile(path, resolvedFileInfo.ModTime(), folderId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fr.log.Error("failed to load dashboard from ", "file", path, "error", err)
|
fr.log.Error("failed to load dashboard from ", "file", path, "error", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbDashboard *models.Dashboard
|
|
||||||
query := &models.GetDashboardQuery{}
|
|
||||||
provisionedData, allReadyProvisioned := provisionedDashboardRefs[path]
|
|
||||||
|
|
||||||
if allReadyProvisioned {
|
if allReadyProvisioned {
|
||||||
dash.Dashboard.SetId(provisionedData.DashboardId)
|
dash.Dashboard.SetId(provisionedData.DashboardId)
|
||||||
|
|
||||||
query.Id = provisionedData.DashboardId
|
|
||||||
} else {
|
|
||||||
if dash.Dashboard.Id != 0 {
|
|
||||||
fr.log.Error("Cannot provision dashboard. Please remove the id property from the json file")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
query.Slug = dash.Dashboard.Slug
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bus.Dispatch(query)
|
fr.log.Debug("saving new dashboard", "file", path)
|
||||||
dbDashboard = query.Result
|
dp := &models.DashboardProvisioning{ExternalId: path, Name: fr.Cfg.Name, Updated: resolvedFileInfo.ModTime()}
|
||||||
|
_, err = fr.dashboardRepo.SaveProvisionedDashboard(dash, dp)
|
||||||
// if we don't have the dashboard in the db, save it!
|
return err
|
||||||
if err == models.ErrDashboardNotFound {
|
|
||||||
fr.log.Debug("saving new dashboard", "file", path)
|
|
||||||
err = saveDashboard(fr, path, dash)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fr.log.Error("failed to query for dashboard", "slug", dash.Dashboard.Slug, "error", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// break if db version is newer then fil version
|
|
||||||
if dbDashboard.Updated.Unix() >= resolvedFileInfo.ModTime().Unix() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fr.log.Debug("loading dashboard from disk into database.", "file", path)
|
|
||||||
return saveDashboard(fr, path, dash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProvisionedDashboardByPath(repo dashboards.Repository, name string) (map[string]*models.DashboardProvisioning, error) {
|
func getProvisionedDashboardByPath(repo dashboards.Repository, name string) (map[string]*models.DashboardProvisioning, error) {
|
||||||
@@ -275,20 +244,6 @@ func createWalkFn(filesOnDisk map[string]os.FileInfo) filepath.WalkFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveDashboard(fr *fileReader, path string, dash *dashboards.SaveDashboardDTO) error {
|
|
||||||
d := &models.DashboardProvisioning{
|
|
||||||
ExternalId: path,
|
|
||||||
Name: fr.Cfg.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := fr.dashboardRepo.SaveProvisionedDashboard(dash, d)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateWalkablePath(fileInfo os.FileInfo) (bool, error) {
|
func validateWalkablePath(fileInfo os.FileInfo) (bool, error) {
|
||||||
if fileInfo.IsDir() {
|
if fileInfo.IsDir() {
|
||||||
if strings.HasPrefix(fileInfo.Name(), ".") {
|
if strings.HasPrefix(fileInfo.Name(), ".") {
|
||||||
@@ -304,7 +259,7 @@ func validateWalkablePath(fileInfo os.FileInfo) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fr *fileReader) readDashboardFromFile(path string, folderId int64) (*dashboards.SaveDashboardDTO, error) {
|
func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time, folderId int64) (*dashboards.SaveDashboardDTO, error) {
|
||||||
reader, err := os.Open(path)
|
reader, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -316,17 +271,10 @@ func (fr *fileReader) readDashboardFromFile(path string, folderId int64) (*dashb
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stat, err := os.Stat(path)
|
dash, err := createDashboardJson(data, lastModified, fr.Cfg, folderId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dash, err := createDashboardJson(data, stat.ModTime(), fr.Cfg, folderId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fr.cache.addDashboardCache(path, dash)
|
|
||||||
|
|
||||||
return dash, nil
|
return dash, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,23 +66,6 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
So(dashboards, ShouldEqual, 2)
|
So(dashboards, ShouldEqual, 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should not update dashboards when db is newer", func() {
|
|
||||||
cfg.Options["path"] = oneDashboard
|
|
||||||
|
|
||||||
fakeRepo.getDashboard = append(fakeRepo.getDashboard, &models.Dashboard{
|
|
||||||
Updated: time.Now().Add(time.Hour),
|
|
||||||
Slug: "grafana",
|
|
||||||
})
|
|
||||||
|
|
||||||
reader, err := NewDashboardFileReader(cfg, logger)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
err = reader.startWalkingDisk()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
So(len(fakeRepo.inserted), ShouldEqual, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("Can read default dashboard and replace old version in database", func() {
|
Convey("Can read default dashboard and replace old version in database", func() {
|
||||||
cfg.Options["path"] = oneDashboard
|
cfg.Options["path"] = oneDashboard
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package sqlstore
|
package sqlstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
@@ -28,6 +26,9 @@ func SaveProvisionedDashboard(cmd *models.SaveProvisionedDashboardCommand) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Result = cmd.DashboardCmd.Result
|
cmd.Result = cmd.DashboardCmd.Result
|
||||||
|
if cmd.DashboardProvisioning.Updated.IsZero() {
|
||||||
|
cmd.DashboardProvisioning.Updated = cmd.Result.Updated
|
||||||
|
}
|
||||||
|
|
||||||
return saveProvionedData(sess, cmd.DashboardProvisioning, cmd.Result)
|
return saveProvionedData(sess, cmd.DashboardProvisioning, cmd.Result)
|
||||||
})
|
})
|
||||||
@@ -42,7 +43,6 @@ func saveProvionedData(sess *DBSession, cmd *models.DashboardProvisioning, dashb
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Id = result.Id
|
cmd.Id = result.Id
|
||||||
cmd.Updated = time.Now()
|
|
||||||
cmd.DashboardId = dashboard.Id
|
cmd.DashboardId = dashboard.Id
|
||||||
|
|
||||||
if exist {
|
if exist {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ func addDashboardMigration(mg *Migrator) {
|
|||||||
{Name: "dashboard_id", Type: DB_BigInt, Nullable: true},
|
{Name: "dashboard_id", Type: DB_BigInt, Nullable: true},
|
||||||
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||||
{Name: "external_id", Type: DB_Text, Nullable: false},
|
{Name: "external_id", Type: DB_Text, Nullable: false},
|
||||||
{Name: "updated", Type: DB_Int, Nullable: false},
|
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||||
},
|
},
|
||||||
Indices: []*Index{
|
Indices: []*Index{
|
||||||
{Cols: []string{"dashboard_id"}},
|
{Cols: []string{"dashboard_id"}},
|
||||||
|
|||||||
Reference in New Issue
Block a user