123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- package poller
-
- import (
- "time"
-
- "config"
- "git"
- "grafana"
- puller "puller"
- "pusher/common"
-
- "github.com/sirupsen/logrus"
- )
-
- // Setup loads (and synchronise if needed) the Git repository mentioned in the
- // configuration file, then creates the poller that will pull from the Git
- // repository on a regular basis and push all the changes to Grafana.
- // Returns an error if the poller encountered one.
- func Setup(cfg *config.Config, client *grafana.Client, delRemoved bool) error {
- // Load the Git repository.
- r, needsSync, err := git.NewRepository(cfg.Git)
- if err != nil {
- return err
- }
-
- // Synchronise the repository if needed.
- if needsSync {
- if err = r.Sync(false); err != nil {
- return err
- }
- }
-
- errs := make(chan error, 1)
-
- // In the future we may want to poll from several Git repositories, so we
- // run the poller in a go routine.
- go func() {
- if err = poller(cfg, r, client, delRemoved); err != nil {
- errs <- err
- return
- }
- }()
-
- err = <-errs
- return err
- }
-
- // poller gets the current status of the Git repository that has previously been
- // loaded, and then starts an infinite loop that will pull from the Git
- // remote, then, if there was any new commit, retrieve the contents of the
- // modified and added files to push them to Grafana. If set by the user via
- // a command-line flag, it will also check for removed files and delete the
- // corresponding dashboards from Grafana. It then sleeps for the time specified
- // in the configuration file, before starting its next iteration.
- // Returns an error if there was an issue checking the Git repository status,
- // synchronising it, reading the files' contents or discussing with the Grafana
- // API
- func poller(
- cfg *config.Config, repo *git.Repository, client *grafana.Client,
- delRemoved bool,
- ) (err error) {
- // Get current state of the repo.
- // This is mainly to give an initial value to variables that will see their
- // content changed with every iteration of the loop.
- latestCommit, err := repo.GetLatestCommit()
- if err != nil {
- return
- }
-
- filesContents, err := repo.GetFilesContentsAtCommit(latestCommit)
- if err != nil {
- return
- }
-
- // We'll need to know the previous commit in order to compare its hash with
- // the one from the most recent commit after we pull from the remote, se we
- // know if there was any new commit.
- previousCommit := latestCommit
- // We need to store the content of the files from the previous iteration of
- // the loop in order to manage removed files which contents won't be
- // accessible anymore.
- previousFilesContents := filesContents
-
- // Start looping
- for {
- // Synchronise the repository (i.e. pull from remote).
- if err = repo.Sync(true); err != nil {
- return
- }
-
- // Retrieve the latest commit in order to compare its hash with the
- // previous one.
- latestCommit, err = repo.GetLatestCommit()
- if err != nil {
- return
- }
-
- // If there is at least one new commit, handle the changes it introduces.
- if previousCommit.Hash.String() != latestCommit.Hash.String() {
- logrus.WithFields(logrus.Fields{
- "previous_hash": previousCommit.Hash.String(),
- "new_hash": latestCommit.Hash.String(),
- }).Info("New commit(s) detected")
-
- // Get the updated files contents.
- filesContents, err = repo.GetFilesContentsAtCommit(latestCommit)
- if err != nil {
- return err
- }
-
- // Get the name of the files that have been added/modified and
- // removed between the two iterations.
- modified, removed, err := repo.GetModifiedAndRemovedFiles(previousCommit, latestCommit)
- if err != nil {
- return err
- }
-
- // Get a map containing the latest known content of each added,
- // modified and removed file.
- mergedContents := mergeContents(modified, removed, filesContents, previousFilesContents)
- // Push the contents of the files that were added or modified to the
- // Grafana API.
- common.PushFiles(modified, mergedContents, client)
-
- // If the user requested it, delete all dashboards that were removed
- // from the repository.
- if delRemoved {
- common.DeleteDashboards(removed, mergedContents, client)
- }
-
- // Grafana will auto-update the version number after we pushed the new
- // dashboards, so we use the puller mechanic to pull the updated numbers and
- // commit them in the git repo.
- if err = puller.PullGrafanaAndCommit(client, cfg); err != nil {
- logrus.WithFields(logrus.Fields{
- "error": err,
- "repo": cfg.Git.User + "@" + cfg.Git.URL,
- "clone_path": cfg.Git.ClonePath,
- }).Error("Call to puller returned an error")
- }
- }
-
- // Update the commit and files contents to prepare for the next iteration.
- previousCommit = latestCommit
- previousFilesContents = filesContents
-
- // Sleep before the next iteration.
- time.Sleep(time.Duration(cfg.Pusher.Config.Interval) * time.Second)
- }
- }
-
- // mergeContents will take as arguments a list of names of files that have been
- // added/modified, a list of names of files that have been removed from the Git
- // repository, the current contents of the files in the Git repository, and the
- // contents of the files in the Git repository as they were at the previous
- // iteration of the poller's loop.
- // It will create and return a map contaning the current content of all
- // added/modified file, and the previous content of all removed file (since
- // they are no longer accessible on disk). All files in this map is either added,
- // modified or removed on the Git repository.
- func mergeContents(
- modified []string, removed []string,
- filesContents map[string][]byte, previousFilesContents map[string][]byte,
- ) (merged map[string][]byte) {
- merged = make(map[string][]byte)
-
- // Load the added/modified files' contents
- for _, modifiedFile := range modified {
- merged[modifiedFile] = filesContents[modifiedFile]
- }
-
- // Load the removed files' contents
- for _, removedFile := range removed {
- merged[removedFile] = previousFilesContents[removedFile]
- }
-
- return
- }
|