refactor(internal/cmd): move backend cmd functions to grafanabackuper package
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
840008df2d
commit
83a15b5a87
7 changed files with 112 additions and 264 deletions
|
@ -2,14 +2,11 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"slices"
|
||||
"os"
|
||||
|
||||
"git.ar21.de/yolokube/grafana-backuper/internal/config"
|
||||
"git.ar21.de/yolokube/grafana-backuper/internal/helper"
|
||||
"git.ar21.de/yolokube/grafana-backuper/pkg/git"
|
||||
"git.ar21.de/yolokube/grafana-backuper/pkg/grafana"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"git.ar21.de/yolokube/grafana-backuper/pkg/grafanabackuper"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -20,12 +17,20 @@ func NewBackupCommand(c *config.Config) *cobra.Command {
|
|||
Long: "Back up the dashboards from grafana to a git repository.",
|
||||
Run: func(cmd *cobra.Command, _ []string) {
|
||||
if err := backup(cmd.Context(), c); err != nil {
|
||||
log.WithContext(cmd.Context()).WithError(err).Fatal()
|
||||
log.Fatal().Err(err).Send()
|
||||
}
|
||||
},
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
if err := c.Validate(); err != nil {
|
||||
log.WithContext(cmd.Context()).WithError(err).Fatal("checking flags & environment variables")
|
||||
errs := c.Validate()
|
||||
for _, err := range errs {
|
||||
log.Error().Err(err).Send()
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
if err := cmd.Help(); err != nil {
|
||||
log.Error().Err(err).Send()
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -37,78 +42,18 @@ func NewBackupCommand(c *config.Config) *cobra.Command {
|
|||
return backupCmd
|
||||
}
|
||||
|
||||
func backup(ctx context.Context, c *config.Config) error {
|
||||
client := grafana.NewClient(
|
||||
c.GrafanaURL,
|
||||
grafana.WithToken(c.GrafanaToken),
|
||||
func backup(ctx context.Context, cfg *config.Config) error {
|
||||
client := grafanabackuper.NewClient(
|
||||
grafanabackuper.WithZerologLogger(cfg.Logger),
|
||||
)
|
||||
|
||||
project, err := helper.PrepareProject(ctx, c)
|
||||
if err != nil {
|
||||
if err := client.GetSigner(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var signer git.SignKey
|
||||
if c.GPGKey != "" {
|
||||
signer = git.SignKey{KeyFile: c.GPGKey}
|
||||
if err = signer.ReadKeyFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dashboards, _, err := client.File.Search(ctx, grafana.WithType(grafana.SearchTypeDashboard))
|
||||
if err != nil {
|
||||
if err := client.Prepare(ctx, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dashboardInfo := range dashboards {
|
||||
var dashboard *grafana.Dashboard
|
||||
dashboard, _, err = client.Dashboard.Get(ctx, dashboardInfo.UID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var versions []*grafana.DashboardVersion
|
||||
versions, _, err = client.DashboardVersion.List(ctx, dashboardInfo.UID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slices.Reverse(versions)
|
||||
|
||||
uncommitedVersion := c.ForceCommits
|
||||
for _, version := range versions {
|
||||
var dashboardVersion *grafana.DashboardVersion
|
||||
dashboardVersion, _, err = client.DashboardVersion.Get(ctx, dashboardInfo.UID, version.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var commitMsg string
|
||||
commitMsg, err = helper.CommitVersion(
|
||||
dashboard,
|
||||
dashboardVersion,
|
||||
dashboardInfo,
|
||||
project,
|
||||
c,
|
||||
signer,
|
||||
uncommitedVersion,
|
||||
)
|
||||
if errors.Is(err, helper.ErrAlreadyCommited) {
|
||||
log.WithContext(ctx).WithField("commit-msg", commitMsg).Info("already committed")
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uncommitedVersion = true
|
||||
log.WithContext(ctx).WithField("commit-msg", commitMsg).Info("commit created")
|
||||
}
|
||||
}
|
||||
|
||||
if !project.HasChanges() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return project.Push(ctx)
|
||||
return client.Backup.Start(ctx, cfg)
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.ar21.de/yolokube/grafana-backuper/internal/config"
|
||||
"git.ar21.de/yolokube/grafana-backuper/internal/logger"
|
||||
"git.ar21.de/yolokube/grafana-backuper/internal/version"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -24,22 +26,27 @@ func NewRootCommand(c *config.Config) *cobra.Command {
|
|||
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||
initializeConfig(cmd)
|
||||
|
||||
c.Output = os.Stdout
|
||||
|
||||
log.SetOutput(c.Output)
|
||||
log.SetLevel(log.InfoLevel)
|
||||
cmd.SetOut(c.Output)
|
||||
// c.Output = os.Stdout
|
||||
cmd.SetOut(os.Stdout)
|
||||
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
if c.Debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
|
||||
if c.JSONFormat {
|
||||
log.Logger = logger.JSONLoggerLayout(os.Stderr)
|
||||
c.Logger = logger.JSONLoggerLayout(os.Stdout)
|
||||
} else {
|
||||
log.Logger = logger.CliLoggerLayout(os.Stderr)
|
||||
c.Logger = logger.CliLoggerLayout(os.Stdout)
|
||||
}
|
||||
},
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
rootCmd.SetErrPrefix("Error:")
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(&c.Debug, "debug", "d", false, "Debug output")
|
||||
rootCmd.PersistentFlags().BoolVar(&c.JSONFormat, "json", false, "JSON output")
|
||||
rootCmd.PersistentFlags().StringVar(&c.GrafanaURL, "grafana-url", "", "Grafana URL to access the API")
|
||||
rootCmd.PersistentFlags().StringVar(&c.GrafanaToken, "grafana-token", "", "Grafana auth token to access the API")
|
||||
rootCmd.PersistentFlags().StringVar(&c.GitBranch, "git-branch", "main", "Git branch name")
|
||||
|
|
|
@ -3,11 +3,14 @@ package config
|
|||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Debug bool
|
||||
ForceCommits bool
|
||||
JSONFormat bool
|
||||
GrafanaURL string
|
||||
GrafanaToken string
|
||||
GitBranch string
|
||||
|
@ -18,34 +21,35 @@ type Config struct {
|
|||
GPGKey string
|
||||
|
||||
Output io.Writer
|
||||
Logger zerolog.Logger
|
||||
}
|
||||
|
||||
func (c *Config) Validate() error {
|
||||
var err error
|
||||
func (c *Config) Validate() []error {
|
||||
var errs []error
|
||||
|
||||
if c.GrafanaURL == "" {
|
||||
err = errors.Join(err, errors.New("invalid grafana URL, must not be blank"))
|
||||
errs = append(errs, errors.New("invalid grafana URL, must not be blank"))
|
||||
}
|
||||
|
||||
if c.GrafanaToken == "" {
|
||||
err = errors.Join(err, errors.New("invalid auth token for grafana, must not be blank"))
|
||||
errs = append(errs, errors.New("invalid auth token for grafana, must not be blank"))
|
||||
}
|
||||
|
||||
if c.GitRepo == "" {
|
||||
err = errors.Join(err, errors.New("invalid repo url for git, must not be blank"))
|
||||
errs = append(errs, errors.New("invalid repo url for git, must not be blank"))
|
||||
}
|
||||
|
||||
if c.GitUser == "" {
|
||||
err = errors.Join(err, errors.New("invalid username for git, must not be blank"))
|
||||
errs = append(errs, errors.New("invalid username for git, must not be blank"))
|
||||
}
|
||||
|
||||
if c.GitPass == "" {
|
||||
err = errors.Join(err, errors.New("invalid password for git, must not be blank"))
|
||||
errs = append(errs, errors.New("invalid password for git, must not be blank"))
|
||||
}
|
||||
|
||||
if c.GitBranch == "" {
|
||||
err = errors.Join(err, errors.New("invalid branch name for git, must not be blank"))
|
||||
errs = append(errs, errors.New("invalid branch name for git, must not be blank"))
|
||||
}
|
||||
|
||||
return err
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.ar21.de/yolokube/grafana-backuper/internal/config"
|
||||
"git.ar21.de/yolokube/grafana-backuper/pkg/git"
|
||||
"git.ar21.de/yolokube/grafana-backuper/pkg/grafana"
|
||||
)
|
||||
|
||||
var ErrAlreadyCommited = errors.New("already committed")
|
||||
|
||||
func CreateCommitMsg(d *grafana.DashboardVersion, title, uid string) string {
|
||||
commitMsg := fmt.Sprintf("%s: Update %s to version %d", title, uid, d.ID)
|
||||
|
||||
if d.Message != "" {
|
||||
commitMsg += fmt.Sprintf(" => %s", d.Message)
|
||||
}
|
||||
|
||||
return commitMsg
|
||||
}
|
||||
|
||||
func CommitVersion(
|
||||
dashboard *grafana.Dashboard,
|
||||
dashboardVersion *grafana.DashboardVersion,
|
||||
dashboardInfo *grafana.SearchResult,
|
||||
project *git.Project,
|
||||
cfg *config.Config,
|
||||
signer git.SignKey,
|
||||
force bool,
|
||||
) (string, error) {
|
||||
commitMsg := CreateCommitMsg(dashboardVersion, dashboardInfo.Title, dashboardInfo.UID)
|
||||
|
||||
if !force && project.CommitExists(dashboardVersion.DashboardUID, dashboardVersion.ID) {
|
||||
return commitMsg, ErrAlreadyCommited
|
||||
}
|
||||
|
||||
UpdateDashboardInfo(dashboard, dashboardVersion)
|
||||
|
||||
data, err := json.MarshalIndent(grafana.SchemaFromDashboardMeta(dashboard), "", " ")
|
||||
if err != nil {
|
||||
return commitMsg, err
|
||||
}
|
||||
|
||||
commitOpts := []git.CommitOption{
|
||||
git.WithAuthor(dashboardVersion.CreatedBy, ""),
|
||||
git.WithFileContent(data, dashboardInfo.Title, dashboard.FolderTitle),
|
||||
git.WithSigner(signer),
|
||||
}
|
||||
|
||||
if cfg.GitUser != "" {
|
||||
commitOpts = append(commitOpts, git.WithCommitter(cfg.GitUser, cfg.GitEmail))
|
||||
}
|
||||
|
||||
commit := project.NewCommit(commitOpts...)
|
||||
return commitMsg, commit.Create(commitMsg)
|
||||
}
|
||||
|
||||
func PrepareProject(ctx context.Context, c *config.Config) (*git.Project, error) {
|
||||
project := git.NewProject(
|
||||
c.GitRepo,
|
||||
git.WithBasicAuth(c.GitUser, c.GitPass),
|
||||
git.WithBranch(c.GitBranch),
|
||||
git.WithOutputWriter(c.Output),
|
||||
)
|
||||
|
||||
if err := project.Clone(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := project.Checkout(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := project.Pull(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func UpdateDashboardInfo(d *grafana.Dashboard, dv *grafana.DashboardVersion) {
|
||||
d.Dashboard = dv.Data
|
||||
d.UpdatedBy = dv.CreatedBy
|
||||
d.Updated = dv.Created
|
||||
d.Version = dv.Version
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue