Commit c817aecd by bergquist

provisioning: only update dashboard if hash of json changed

parent 44f5b92f
...@@ -5,7 +5,7 @@ bulkDashboard() { ...@@ -5,7 +5,7 @@ bulkDashboard() {
requiresJsonnet requiresJsonnet
COUNTER=0 COUNTER=0
MAX=400 MAX=4
while [ $COUNTER -lt $MAX ]; do while [ $COUNTER -lt $MAX ]; do
jsonnet -o "dashboards/bulk-testing/dashboard${COUNTER}.json" -e "local bulkDash = import 'dashboards/bulk-testing/bulkdash.jsonnet'; bulkDash + { uid: 'uid-${COUNTER}', title: 'title-${COUNTER}' }" jsonnet -o "dashboards/bulk-testing/dashboard${COUNTER}.json" -e "local bulkDash = import 'dashboards/bulk-testing/bulkdash.jsonnet'; bulkDash + { uid: 'uid-${COUNTER}', title: 'title-${COUNTER}' }"
let COUNTER=COUNTER+1 let COUNTER=COUNTER+1
......
...@@ -254,6 +254,7 @@ type DashboardProvisioning struct { ...@@ -254,6 +254,7 @@ type DashboardProvisioning struct {
DashboardId int64 DashboardId int64
Name string Name string
ExternalId string ExternalId string
CheckSum string
Updated int64 Updated int64
} }
......
...@@ -4,12 +4,14 @@ import ( ...@@ -4,12 +4,14 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
...@@ -161,13 +163,18 @@ func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.Fil ...@@ -161,13 +163,18 @@ func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.Fil
provisionedData, alreadyProvisioned := provisionedDashboardRefs[path] provisionedData, alreadyProvisioned := provisionedDashboardRefs[path]
upToDate := alreadyProvisioned && provisionedData.Updated >= resolvedFileInfo.ModTime().Unix() upToDate := alreadyProvisioned && provisionedData.Updated >= resolvedFileInfo.ModTime().Unix()
dash, err := fr.readDashboardFromFile(path, resolvedFileInfo.ModTime(), folderId) jsonFile, 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 provisioningMetadata, nil return provisioningMetadata, nil
} }
if provisionedData != nil && jsonFile.checkSum == provisionedData.CheckSum {
upToDate = true
}
// keeps track of what uid's and title's we have already provisioned // keeps track of what uid's and title's we have already provisioned
dash := jsonFile.dashboard
provisioningMetadata.uid = dash.Dashboard.Uid provisioningMetadata.uid = dash.Dashboard.Uid
provisioningMetadata.title = dash.Dashboard.Title provisioningMetadata.title = dash.Dashboard.Title
...@@ -185,7 +192,13 @@ func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.Fil ...@@ -185,7 +192,13 @@ func (fr *fileReader) saveDashboard(path string, folderId int64, fileInfo os.Fil
} }
fr.log.Debug("saving new dashboard", "file", path) fr.log.Debug("saving new dashboard", "file", path)
dp := &models.DashboardProvisioning{ExternalId: path, Name: fr.Cfg.Name, Updated: resolvedFileInfo.ModTime().Unix()} dp := &models.DashboardProvisioning{
ExternalId: path,
Name: fr.Cfg.Name,
Updated: resolvedFileInfo.ModTime().Unix(),
CheckSum: jsonFile.checkSum,
}
_, err = fr.dashboardService.SaveProvisionedDashboard(dash, dp) _, err = fr.dashboardService.SaveProvisionedDashboard(dash, dp)
return provisioningMetadata, err return provisioningMetadata, err
} }
...@@ -283,14 +296,30 @@ func validateWalkablePath(fileInfo os.FileInfo) (bool, error) { ...@@ -283,14 +296,30 @@ func validateWalkablePath(fileInfo os.FileInfo) (bool, error) {
return true, nil return true, nil
} }
func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time, folderId int64) (*dashboards.SaveDashboardDTO, error) { type dashboardJsonFile struct {
dashboard *dashboards.SaveDashboardDTO
checkSum string
lastModified time.Time
}
func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time, folderId int64) (*dashboardJsonFile, error) {
reader, err := os.Open(path) reader, err := os.Open(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer reader.Close() defer reader.Close()
data, err := simplejson.NewFromReader(reader) all, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
checkSum, err := util.Md5SumString(string(all))
if err != nil {
return nil, err
}
data, err := simplejson.NewJson(all)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -300,7 +329,11 @@ func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time, ...@@ -300,7 +329,11 @@ func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time,
return nil, err return nil, err
} }
return dash, nil return &dashboardJsonFile{
dashboard: dash,
checkSum: checkSum,
lastModified: lastModified,
}, nil
} }
type provisioningMetadata struct { type provisioningMetadata struct {
...@@ -328,7 +361,6 @@ func (checker provisioningSanityChecker) track(pm provisioningMetadata) { ...@@ -328,7 +361,6 @@ func (checker provisioningSanityChecker) track(pm provisioningMetadata) {
if len(pm.title) > 0 { if len(pm.title) > 0 {
checker.titleUsage[pm.title] += 1 checker.titleUsage[pm.title] += 1
} }
} }
func (checker provisioningSanityChecker) logWarnings(log log.Logger) { func (checker provisioningSanityChecker) logWarnings(log log.Logger) {
...@@ -343,5 +375,4 @@ func (checker provisioningSanityChecker) logWarnings(log log.Logger) { ...@@ -343,5 +375,4 @@ func (checker provisioningSanityChecker) logWarnings(log log.Logger) {
log.Error("the same 'title' is used more than once", "title", title, "provider", checker.provisioningProvider) log.Error("the same 'title' is used more than once", "title", title, "provider", checker.provisioningProvider)
} }
} }
} }
...@@ -211,4 +211,8 @@ func addDashboardMigration(mg *Migrator) { ...@@ -211,4 +211,8 @@ func addDashboardMigration(mg *Migrator) {
"name": "name", "name": "name",
"external_id": "external_id", "external_id": "external_id",
}) })
mg.AddMigration("Add check_sum column", NewAddColumnMigration(dashboardExtrasTableV2, &Column{
Name: "check_sum", Type: DB_NVarchar, Length: 32, Nullable: true,
}))
} }
package util
import (
"crypto/md5"
"encoding/hex"
"io"
"strings"
)
// Md5Sum calculates the md5sum of a stream
func Md5Sum(reader io.Reader) (string, error) {
var returnMD5String string
hash := md5.New()
if _, err := io.Copy(hash, reader); err != nil {
return returnMD5String, err
}
hashInBytes := hash.Sum(nil)[:16]
returnMD5String = hex.EncodeToString(hashInBytes)
return returnMD5String, nil
}
// Md5Sum calculates the md5sum of a string
func Md5SumString(input string) (string, error) {
buffer := strings.NewReader(input)
return Md5Sum(buffer)
}
package util
import "testing"
func TestMd5Sum(t *testing.T) {
input := "dont hash passwords with md5"
have, err := Md5SumString(input)
if err != nil {
t.Fatal("expected err to be nil")
}
want := "2d6a56c82d09d374643b926d3417afba"
if have != want {
t.Fatalf("expected: %s got: %s", want, have)
}
}
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