123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- package main
-
- import (
- "bytes"
- "encoding/json"
- "io/ioutil"
- "os"
-
- "git"
- "grafana"
-
- gogit "gopkg.in/src-d/go-git.v4"
- )
-
- // diffVersion represents a dashboard version diff.
- type diffVersion struct {
- oldVersion int
- newVersion int
- }
-
- // PullGrafanaAndCommit pulls all the dashboards from Grafana then commits each
- // of them to Git except for those that have a newer or equal version number
- // already versionned in the repo
- func PullGrafanaAndCommit(
- client *grafana.Client,
- repoURL string, clonePath string, privateKeyPath string,
- ) error {
- dv := make(map[string]diffVersion)
-
- dbVersions, err := getDashboardsVersions()
- if err != nil {
- return err
- }
-
- repo, err := git.Sync(repoURL, clonePath, privateKeyPath)
- if err != nil {
- return err
- }
-
- w, err := repo.Worktree()
- if err != nil {
- return err
- }
-
- uris, err := client.GetDashboardsURIs()
- if err != nil {
- return err
- }
-
- for _, uri := range uris {
- dashboard, err := client.GetDashboard(uri)
- if err != nil {
- return err
- }
-
- version, ok := dbVersions[dashboard.Slug]
- if !ok || dashboard.Version > version {
- if err = addDashboardChangesToRepo(dashboard, w); err != nil {
- return err
- }
-
- dv[dashboard.Slug] = diffVersion{
- oldVersion: version,
- newVersion: dashboard.Version,
- }
- }
- }
-
- status, err := w.Status()
- if err != nil {
- return err
- }
-
- if !status.IsClean() {
- if err = commitNewVersions(dbVersions, dv, w); err != nil {
- return err
- }
- }
-
- if err = git.Push(repo, privateKeyPath); err != nil {
- return err
- }
-
- return nil
- }
-
- // addDashboardChangesToRepo writes a dashboard content in a file, then adds the
- // file to the git index so it can be comitted afterwards.
- // Returns an error if there was an issue with either of the steps.
- func addDashboardChangesToRepo(
- dashboard *grafana.Dashboard, worktree *gogit.Worktree,
- ) error {
- slugExt := dashboard.Slug + ".json"
- if err := rewriteFile(
- *clonePath+"/"+slugExt,
- dashboard.RawJSON,
- ); err != nil {
- return err
- }
-
- if _, err := worktree.Add(slugExt); err != nil {
- return err
- }
-
- return nil
- }
-
- // rewriteFile removes a given file and re-creates it with a new content. The
- // content is provided as JSON, and is then indented before being written down.
- // We need the whole "remove then recreate" thing because, if the file already
- // exists, ioutil.WriteFile will append the content to it. However, we want to
- // replace the oldest version with another (so git can diff it), so we re-create
- // the file with the changed content.
- // Returns an error if there was an issue when removing or writing the file, or
- // indenting the JSON content.
- func rewriteFile(filename string, content []byte) error {
- if err := os.Remove(filename); err != nil {
- pe, ok := err.(*os.PathError)
- if !ok || pe.Err.Error() != "no such file or directory" {
- return err
- }
- }
-
- indentedContent, err := indent(content)
- if err != nil {
- return err
- }
-
- return ioutil.WriteFile(filename, indentedContent, 0644)
- }
-
- // indent indents a given JSON content with tabs.
- // We need to indent the content as the Grafana API returns a one-lined JSON
- // string, which isn't great to work with.
- // Returns an error if there was an issue with the process.
- func indent(srcJSON []byte) (indentedJSON []byte, err error) {
- buf := bytes.NewBuffer(nil)
- if err = json.Indent(buf, srcJSON, "", "\t"); err != nil {
- return
- }
-
- indentedJSON, err = ioutil.ReadAll(buf)
- return
- }
|