package cmd import ( "context" "encoding/json" "fmt" "slices" "git.ar21.de/yolokube/grafana-backuper/internal/config" "git.ar21.de/yolokube/grafana-backuper/pkg/git" "git.ar21.de/yolokube/grafana-backuper/pkg/grafana" "github.com/spf13/cobra" ) func NewBackupCommand(c *config.Config) *cobra.Command { backupCmd := &cobra.Command{ Use: "backup", Short: "Back up the dashboards from grafana to a git repository.", Long: "Back up the dashboards from grafana to a git repository.", RunE: func(cmd *cobra.Command, _ []string) error { return backup(cmd.Context(), c) }, PreRunE: func(_ *cobra.Command, _ []string) error { return c.Validate() }, } backupCmd.PersistentFlags().BoolVar(&c.ForceCommits, "force", false, "Force git commits / ignore existence check") backupCmd.PersistentFlags().StringVar(&c.GitEmail, "git-email", "", "Git email address") backupCmd.PersistentFlags().StringVar(&c.GPGKey, "signing-key", "", "Path to the GPG signing key") return backupCmd } func backup(ctx context.Context, c *config.Config) error { client := grafana.NewClient( c.GrafanaURL, grafana.WithToken(c.GrafanaToken), ) 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 err } if err := project.Checkout(); err != nil { return err } if err := project.Pull(ctx); 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 { 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) var uncommitedVersion bool for _, version := range versions { var dashboardVersion *grafana.DashboardVersion dashboardVersion, _, err = client.DashboardVersion.Get(ctx, dashboardInfo.UID, version.Version) if err != nil { return err } dashboard.Dashboard = dashboardVersion.Data dashboard.UpdatedBy = dashboardVersion.CreatedBy dashboard.Updated = dashboardVersion.Created dashboard.Version = dashboardVersion.Version var data []byte data, err = json.MarshalIndent(grafana.SchemaFromDashboardMeta(dashboard), "", " ") if err != nil { return err } commitMsg := fmt.Sprintf( "%s: Update %s to version %d", dashboardInfo.Title, dashboardInfo.UID, dashboardVersion.ID, ) if dashboardVersion.Message != "" { commitMsg += fmt.Sprintf(" => %s", dashboardVersion.Message) } commitOpts := []git.CommitOption{ git.WithAuthor(dashboardVersion.CreatedBy, ""), git.WithFileContent(data, dashboardInfo.Title, dashboard.FolderTitle), git.WithSigner(signer), } if c.GitUser != "" { commitOpts = append(commitOpts, git.WithCommitter(c.GitUser, c.GitEmail)) } commit := project.NewCommit(commitOpts...) if commit.Exists(dashboardVersion.DashboardUID, dashboardVersion.ID) && !c.ForceCommits && !uncommitedVersion { fmt.Fprintf(c.Output, "%s -> already committed\n", commitMsg) continue } uncommitedVersion = true if err = commit.Create(commitMsg); err != nil { return err } fmt.Fprintln(c.Output, commitMsg) } } if project.HasChanges() { if err = project.Push(ctx); err != nil { return err } } return nil }