Skip to content

Commit ad5c8f2

Browse files
hossainemruztamalsaha
authored andcommitted
Add support for backup cluster resources YAML (#721)
1 parent 2bb8ec3 commit ad5c8f2

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

backup_cluster.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package cmds
2+
3+
import (
4+
"path/filepath"
5+
6+
"github.com/appscode/go/flags"
7+
"github.com/appscode/go/log"
8+
"github.com/spf13/cobra"
9+
"k8s.io/client-go/tools/clientcmd"
10+
"kmodules.xyz/client-go/tools/backup"
11+
"stash.appscode.dev/stash/pkg/restic"
12+
"stash.appscode.dev/stash/pkg/util"
13+
)
14+
15+
const (
16+
JobClusterBackup = "stash-cluster-backup"
17+
)
18+
19+
type clusterBackupOptions struct {
20+
sanitize bool
21+
backupDir string
22+
masterURL string
23+
kubeconfigPath string
24+
context string
25+
outputDir string
26+
27+
backupOpt restic.BackupOptions
28+
setupOpt restic.SetupOptions
29+
metrics restic.MetricsOptions
30+
}
31+
32+
func NewCmdBackupCluster() *cobra.Command {
33+
opt := &clusterBackupOptions{
34+
35+
setupOpt: restic.SetupOptions{
36+
ScratchDir: restic.DefaultScratchDir,
37+
EnableCache: false,
38+
},
39+
backupOpt: restic.BackupOptions{
40+
Host: restic.DefaultHost,
41+
},
42+
metrics: restic.MetricsOptions{
43+
JobName: JobClusterBackup,
44+
},
45+
backupDir: filepath.Join(restic.DefaultScratchDir, "cluster-resources"),
46+
sanitize: false,
47+
}
48+
49+
cmd := &cobra.Command{
50+
Use: "backup-cluster",
51+
Short: "Takes a backup of Cluster's resources YAML",
52+
DisableAutoGenTag: true,
53+
RunE: func(cmd *cobra.Command, args []string) error {
54+
flags.EnsureRequiredFlags(cmd, "provider", "secret-dir")
55+
err := opt.runClusterBackup()
56+
if err != nil {
57+
log.Errorln(err)
58+
return handleResticError(opt.outputDir, restic.DefaultOutputFileName, err)
59+
}
60+
return nil
61+
},
62+
}
63+
cmd.Flags().StringVar(&opt.masterURL, "master", "", "URL of master node")
64+
cmd.Flags().StringVar(&opt.kubeconfigPath, "kubeconfig", opt.kubeconfigPath, "kubeconfig file pointing at the 'core' kubernetes server")
65+
cmd.Flags().StringVar(&opt.context, "context", "", "Context to use from kubeconfig file")
66+
cmd.Flags().BoolVar(&opt.sanitize, "sanitize", false, "Cleanup decorators from dumped YAML files")
67+
68+
cmd.Flags().StringVar(&opt.setupOpt.Provider, "provider", opt.setupOpt.Provider, "Backend provider (i.e. gcs, s3, azure etc)")
69+
cmd.Flags().StringVar(&opt.setupOpt.Bucket, "bucket", opt.setupOpt.Bucket, "Name of the cloud bucket/container (keep empty for local backend)")
70+
cmd.Flags().StringVar(&opt.setupOpt.Endpoint, "endpoint", opt.setupOpt.Endpoint, "Endpoint for s3/s3 compatible backend")
71+
cmd.Flags().StringVar(&opt.setupOpt.URL, "rest-server-url", opt.setupOpt.URL, "URL for rest backend")
72+
cmd.Flags().StringVar(&opt.setupOpt.Path, "path", opt.setupOpt.Path, "Directory inside the bucket where backup will be stored")
73+
cmd.Flags().StringVar(&opt.setupOpt.SecretDir, "secret-dir", opt.setupOpt.SecretDir, "Directory where storage secret has been mounted")
74+
cmd.Flags().StringVar(&opt.setupOpt.ScratchDir, "scratch-dir", opt.setupOpt.ScratchDir, "Temporary directory")
75+
cmd.Flags().BoolVar(&opt.setupOpt.EnableCache, "enable-cache", opt.setupOpt.EnableCache, "Specify weather to enable caching for restic")
76+
cmd.Flags().IntVar(&opt.setupOpt.MaxConnections, "max-connections", opt.setupOpt.MaxConnections, "Specify maximum concurrent connections for GCS, Azure and B2 backend")
77+
78+
cmd.Flags().StringVar(&opt.backupOpt.Host, "hostname", opt.backupOpt.Host, "Name of the host machine")
79+
80+
cmd.Flags().IntVar(&opt.backupOpt.RetentionPolicy.KeepLast, "retention-keep-last", opt.backupOpt.RetentionPolicy.KeepLast, "Specify value for retention strategy")
81+
cmd.Flags().IntVar(&opt.backupOpt.RetentionPolicy.KeepHourly, "retention-keep-hourly", opt.backupOpt.RetentionPolicy.KeepHourly, "Specify value for retention strategy")
82+
cmd.Flags().IntVar(&opt.backupOpt.RetentionPolicy.KeepDaily, "retention-keep-daily", opt.backupOpt.RetentionPolicy.KeepDaily, "Specify value for retention strategy")
83+
cmd.Flags().IntVar(&opt.backupOpt.RetentionPolicy.KeepWeekly, "retention-keep-weekly", opt.backupOpt.RetentionPolicy.KeepWeekly, "Specify value for retention strategy")
84+
cmd.Flags().IntVar(&opt.backupOpt.RetentionPolicy.KeepMonthly, "retention-keep-monthly", opt.backupOpt.RetentionPolicy.KeepMonthly, "Specify value for retention strategy")
85+
cmd.Flags().IntVar(&opt.backupOpt.RetentionPolicy.KeepYearly, "retention-keep-yearly", opt.backupOpt.RetentionPolicy.KeepYearly, "Specify value for retention strategy")
86+
cmd.Flags().StringSliceVar(&opt.backupOpt.RetentionPolicy.KeepTags, "retention-keep-tags", opt.backupOpt.RetentionPolicy.KeepTags, "Specify value for retention strategy")
87+
cmd.Flags().BoolVar(&opt.backupOpt.RetentionPolicy.Prune, "retention-prune", opt.backupOpt.RetentionPolicy.Prune, "Specify weather to prune old snapshot data")
88+
cmd.Flags().BoolVar(&opt.backupOpt.RetentionPolicy.DryRun, "retention-dry-run", opt.backupOpt.RetentionPolicy.DryRun, "Specify weather to test retention policy without deleting actual data")
89+
90+
cmd.Flags().StringVar(&opt.outputDir, "output-dir", opt.outputDir, "Directory where output.json file will be written (keep empty if you don't need to write output in file)")
91+
92+
cmd.Flags().BoolVar(&opt.metrics.Enabled, "metrics-enabled", opt.metrics.Enabled, "Specify weather to export Prometheus metrics")
93+
cmd.Flags().StringVar(&opt.metrics.PushgatewayURL, "metrics-pushgateway-url", opt.metrics.PushgatewayURL, "Pushgateway URL where the metrics will be pushed")
94+
cmd.Flags().StringVar(&opt.metrics.MetricFileDir, "metrics-dir", opt.metrics.MetricFileDir, "Directory where to write metric.prom file (keep empty if you don't want to write metric in a text file)")
95+
cmd.Flags().StringSliceVar(&opt.metrics.Labels, "metrics-labels", opt.metrics.Labels, "Labels to apply in exported metrics")
96+
97+
return cmd
98+
}
99+
100+
func (opt *clusterBackupOptions) runClusterBackup() error {
101+
config, err := clientcmd.BuildConfigFromFlags(opt.masterURL, opt.kubeconfigPath)
102+
if err != nil {
103+
return err
104+
}
105+
106+
// if no explicit context is provided then try to detect context from kubeconfig file.
107+
if opt.context == "" {
108+
cfg, err := clientcmd.LoadFromFile(opt.kubeconfigPath)
109+
if err == nil {
110+
opt.context = cfg.CurrentContext
111+
} else {
112+
// using in-cluster config. so no context. use default.
113+
opt.context = "default"
114+
}
115+
}
116+
117+
// backup cluster resources yaml into opt.backupDir
118+
mgr := backup.NewBackupManager(opt.context, config, opt.sanitize)
119+
120+
_, err = mgr.BackupToDir(opt.backupDir)
121+
if err != nil {
122+
return err
123+
}
124+
125+
// apply nice, ionice settings from env
126+
opt.setupOpt.Nice, err = util.NiceSettingsFromEnv()
127+
if err != nil {
128+
return handleResticError(opt.outputDir, restic.DefaultOutputFileName, err)
129+
}
130+
opt.setupOpt.IONice, err = util.IONiceSettingsFromEnv()
131+
if err != nil {
132+
return handleResticError(opt.outputDir, restic.DefaultOutputFileName, err)
133+
}
134+
135+
// init restic wrapper
136+
resticWrapper, err := restic.NewResticWrapper(opt.setupOpt)
137+
if err != nil {
138+
return err
139+
}
140+
141+
// Now backup the directory where dumped YAML is stored
142+
opt.backupOpt.BackupDirs = []string{opt.backupDir}
143+
backupOutput, backupErr := resticWrapper.RunBackup(opt.backupOpt)
144+
// If metrics are enabled then generate metrics
145+
if opt.metrics.Enabled {
146+
err := backupOutput.HandleMetrics(&opt.metrics, backupErr)
147+
if err != nil {
148+
return err
149+
}
150+
}
151+
if backupErr != nil {
152+
return backupErr
153+
}
154+
155+
// If output directory specified, then write the output in "output.json" file in the specified directory
156+
if opt.outputDir != "" {
157+
return backupOutput.WriteOutput(filepath.Join(opt.outputDir, restic.DefaultOutputFileName))
158+
}
159+
return nil
160+
}

root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ func NewRootCmd() *cobra.Command {
7777

7878
rootCmd.AddCommand(stash_cli.NewCLICmd())
7979
rootCmd.AddCommand(docker.NewDockerCmd())
80+
rootCmd.AddCommand(NewCmdBackupCluster())
8081

8182
return rootCmd
8283
}

0 commit comments

Comments
 (0)