Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Additional restic functionality #20

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ Besides creating backups, `brudi` can also be used to restore your data from bac
- [Redis](#redis)
- [Restic](#restic)
- [Forget](#forget)
- [Snapshots](#snapshots)
- [Check](#check)
- [Prune](#prune)
- [Rebuild-Index](#rebuild-index)
- [Tags](#tags)
- [Sensitive data: Environment variables](#sensitive-data--environment-variables)
- [Restoring from backup](#restoring-from-backup)
- [TarRestore](#tarrestore)
Expand Down Expand Up @@ -277,6 +282,82 @@ restic:
ids: []
```

##### Snapshots

It's possible to run `restic snapshots`-cmd after executing `restic backup` with `brudi` by using `--restic-snapshots`.
The `snapshots`-options are defined in the configuration `.yaml` for brudi.

```yaml
restic:
global:
flags:
# you can provide the repository also via RESTIC_REPOSITORY
repo: "s3:s3.eu-central-1.amazonaws.com/your.s3.bucket/myResticRepo"
backup:
flags:
# in case there is no hostname given, the hostname from source backup is used
hostname: "MyHost"
# these paths are backuped additionally to your given source backup
paths: []
snapshots:
flags:
host: "MyHost"
```

##### Check

It's possible to run `restic check`-cmd after executing `restic backup` with `brudi` by using `--restic-check`.
The `check`-options are defined in the configuration `.yaml` for brudi.

```yaml
restic:
global:
flags:
# you can provide the repository also via RESTIC_REPOSITORY
repo: "s3:s3.eu-central-1.amazonaws.com/your.s3.bucket/myResticRepo"
backup:
flags:
# in case there is no hostname given, the hostname from source backup is used
hostname: "MyHost"
# these paths are backuped additionally to your given source backup
paths: []
check:
checkUnused: true
```

##### Prune

It's possible to run `restic prune`-cmd after executing `restic backup` with `brudi` by using `--restic-prune`.
The `prune`-cmd has no specific options

##### Rebuild-Index
It's possible to run `restic rebuild-index`-cmd after executing `restic backup` with `brudi` by using `--restic-rebuild-index`.
he `rebuild-index`-cmd has no specific options

##### Tags

It's possible to run `restic tags`-cmd after executing `restic backup` with `brudi` by using `--restic-tags`.
The `tags`-options are defined in the configuration `.yaml` for brudi.

```yaml
restic:
global:
flags:
# you can provide the repository also via RESTIC_REPOSITORY
repo: "s3:s3.eu-central-1.amazonaws.com/your.s3.bucket/myResticRepo"
backup:
flags:
# in case there is no hostname given, the hostname from source backup is used
hostname: "MyHost"
# these paths are backuped additionally to your given source backup
paths: []
tags:
flags:
add: ["TestTag"]
host: "MyHost"
ids: []
```

#### Sensitive data: Environment variables

In case you don't want to provide data directly in the `.yaml`-file, e.g. sensitive data like passwords, you can use environment-variables.
Expand Down Expand Up @@ -478,5 +559,10 @@ It is also possible to specify concrete snapshot-ids instead of `latest`.
- [x] `restic backup`
- [x] `restic forget`
- [x] `restic restore`
- [x] `restic snapshots`
- [x] `restic prune`
- [x] `restic check`
- [x] `restic rebuild index`
- [x] `restic taga`
- [x] `storage`
- [x] `s3`
2 changes: 1 addition & 1 deletion cmd/mongodump.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var (
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := source.DoBackupForKind(ctx, mongodump.Kind, cleanup, useRestic, useResticForget)
err := source.DoBackupForKind(ctx, mongodump.Kind, extaResticFlags, cleanup, useRestic, useResticForget)
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/mysqldump.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var (
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := source.DoBackupForKind(ctx, mysqldump.Kind, cleanup, useRestic, useResticForget)
err := source.DoBackupForKind(ctx, mysqldump.Kind, extaResticFlags, cleanup, useRestic, useResticForget)
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/pgdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var (
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := source.DoBackupForKind(ctx, pgdump.Kind, cleanup, useRestic, useResticForget)
err := source.DoBackupForKind(ctx, pgdump.Kind, extaResticFlags, cleanup, useRestic, useResticForget)
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/redisdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var (
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := source.DoBackupForKind(ctx, redisdump.Kind, cleanup, useRestic, useResticForget)
err := source.DoBackupForKind(ctx, redisdump.Kind, extaResticFlags, cleanup, useRestic, useResticForget)
if err != nil {
panic(err)
}
Expand Down
16 changes: 16 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var (
useRestic bool
useResticForget bool
cleanup bool
extaResticFlags config.ExtraResticFlags

rootCmd = &cobra.Command{
Use: "brudi",
Expand All @@ -37,6 +38,21 @@ func init() {

rootCmd.PersistentFlags().BoolVar(&cleanup, "cleanup", false, "cleanup backup files afterwards")

rootCmd.PersistentFlags().BoolVar(&extaResticFlags.ResticList, "restic-snapshots", false,
"list snapshots in restic repository afterwards")

rootCmd.PersistentFlags().BoolVar(&extaResticFlags.ResticCheck, "restic-check", false,
"perform 'restic check' on the repository")

rootCmd.PersistentFlags().BoolVar(&extaResticFlags.ResticPrune, "restic-prune", false,
"perform 'restic prune' on the repository")

rootCmd.PersistentFlags().BoolVar(&extaResticFlags.ResticRebuild, "restic-rebuild-index", false,
"perform 'restic rebuild-index' on the repository")

rootCmd.PersistentFlags().BoolVar(&extaResticFlags.ResticTags, "restic-tags", false,
"executes 'restic tags' after backing up things with restic")

rootCmd.PersistentFlags().StringSliceVarP(&cfgFiles, "config", "c", []string{}, "config file (default is ${HOME}/.brudi.yaml)")
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var (
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err := source.DoBackupForKind(ctx, tar.Kind, cleanup, useRestic, useResticForget)
err := source.DoBackupForKind(ctx, tar.Kind, extaResticFlags, cleanup, useRestic, useResticForget)
if err != nil {
panic(err)
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ const (
KeyOptionsFlags = "options.flags"
KeyOptionsAdditionalArgs = "options.additionalArgs"
)

type ExtraResticFlags struct {
ResticList bool
ResticCheck bool
ResticPrune bool
ResticRebuild bool
ResticTags bool
}
77 changes: 77 additions & 0 deletions pkg/restic/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Client struct {
Config *Config
}

// NewResticClient creates a new restic client with the given hostname and backup paths
func NewResticClient(logger *log.Entry, hostname string, backupPaths ...string) (*Client, error) {
conf := &Config{
Global: &GlobalOptions{
Expand All @@ -26,6 +27,15 @@ func NewResticClient(logger *log.Entry, hostname string, backupPaths ...string)
Flags: &ForgetFlags{},
IDs: []string{},
},
Snapshots: &SnapshotOptions{
Flags: &SnapshotFlags{},
IDs: []string{},
},
Tags: &TagOptions{
Flags: &TagFlags{},
IDs: []string{},
},
Check: &CheckFlags{},
Restore: &RestoreOptions{
Flags: &RestoreFlags{},
ID: "",
Expand All @@ -51,6 +61,7 @@ func NewResticClient(logger *log.Entry, hostname string, backupPaths ...string)
}, nil
}

// DoResticBackup executes initBackup and CreateBackup with the settings from c
func (c *Client) DoResticBackup(ctx context.Context) error {
c.Logger.Info("running 'restic backup'")

Expand All @@ -73,6 +84,7 @@ func (c *Client) DoResticBackup(ctx context.Context) error {
return nil
}

// DoResticRestore executes RestoreBackup with the settings from c
func (c *Client) DoResticRestore(ctx context.Context, backupPath string) error {
c.Logger.Info("running 'restic restore'")
_, err := RestoreBackup(ctx, c.Config.Global, c.Config.Restore, false)
Expand All @@ -82,6 +94,7 @@ func (c *Client) DoResticRestore(ctx context.Context, backupPath string) error {
return nil
}

// DoResticForget executes Forget with the settings of c
func (c *Client) DoResticForget(ctx context.Context) error {
c.Logger.Info("running 'restic forget'")

Expand All @@ -96,3 +109,67 @@ func (c *Client) DoResticForget(ctx context.Context) error {

return nil
}

// DoResticListSnapshots executes ListSnapshots with the settings from c
func (c *Client) DoResticListSnapshots(ctx context.Context) error {
c.Logger.Info("running 'restic snapshots'")

output, err := ListSnapshots(ctx, c.Config.Global, c.Config.Snapshots)
if err != nil {
return errors.WithStack(err)
}
fmt.Println("output of 'restic snapshots':")
for index := range output {
fmt.Printf("ID: %d; Time: %s; Host: %s; Tags: %s; Paths: %s\n",
output[index].ID, output[index].Time, output[index].Hostname, output[index].Tags, output[index].Paths)
}
return nil
}

// DoResticCheck executes Check with the settings from c
func (c *Client) DoResticCheck(ctx context.Context) error {
c.Logger.Info("running 'restic check'")

output, err := Check(ctx, c.Config.Global, c.Config.Check)
if err != nil {
return errors.WithStack(fmt.Errorf("%s - %s", err.Error(), output))
}
fmt.Println(string(output))
return nil
}

// DoResticPruneRepo executes Prune with the settings from c
func (c *Client) DoResticPruneRepo(ctx context.Context) error {
c.Logger.Info("running 'restic prune")

output, err := Prune(ctx, c.Config.Global)
if err != nil {
return errors.WithStack(fmt.Errorf("%s - %s", err.Error(), output))
}
fmt.Println(string(output))
return nil
}

// DoResticRebuildIndex executes RebuildIndex with the settings from c
func (c *Client) DoResticRebuildIndex(ctx context.Context) error {
c.Logger.Info("running 'restic rebuild-index'")

output, err := RebuildIndex(ctx, c.Config.Global)
if err != nil {
return errors.WithStack(fmt.Errorf("%s - %s", err.Error(), output))
}
fmt.Println(string(output))
return nil
}

// DoResticTag executes Tag with the settings from c
func (c *Client) DoResticTag(ctx context.Context) error {
c.Logger.Info("running 'restic rebuild-index'")

output, err := Tag(ctx, c.Config.Global, c.Config.Tags)
if err != nil {
return errors.WithStack(fmt.Errorf("%s - %s", err.Error(), output))
}
fmt.Println(string(output))
return nil
}
44 changes: 30 additions & 14 deletions pkg/restic/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var (
cmdTimeout = 6 * time.Hour
)

// InitBackup executes "restic init"
// initBackup executes "restic init"
func initBackup(ctx context.Context, globalOpts *GlobalOptions) ([]byte, error) {
cmd := newCommand("init", cli.StructToCLI(globalOpts)...)

Expand Down Expand Up @@ -256,11 +256,17 @@ func GetSnapshotSizeByPath(ctx context.Context, snapshotID, path string) (size u
}

// ListSnapshots executes "restic snapshots"
func ListSnapshots(ctx context.Context, opts *SnapshotOptions) ([]Snapshot, error) {
cmd := newCommand("snapshots", cli.StructToCLI(&opts)...)

func ListSnapshots(ctx context.Context, glob *GlobalOptions, opts *SnapshotOptions) ([]Snapshot, error) {
args := append([]string{"--json"}, cli.StructToCLI(opts)...)
args = append(args, cli.StructToCLI(glob)...)
cmd := cli.CommandType{
Binary: binary,
Command: "snapshots",
Args: args,
}
out, err := cli.Run(ctx, cmd)
if err != nil {
fmt.Println(string(out))
return nil, err
}
var snapshots []Snapshot
Expand Down Expand Up @@ -288,8 +294,12 @@ func Find(ctx context.Context, opts *FindOptions) ([]FindResult, error) {
}

// Check executes "restic check"
func Check(ctx context.Context, flags *CheckFlags) ([]byte, error) {
cmd := newCommand("check", cli.StructToCLI(flags)...)
func Check(ctx context.Context, glob *GlobalOptions, flags *CheckFlags) ([]byte, error) {
cmd := cli.CommandType{
Binary: binary,
Command: "check",
Args: append(cli.StructToCLI(glob), cli.StructToCLI(flags)...),
}
return cli.Run(ctx, cmd)
}

Expand Down Expand Up @@ -332,20 +342,23 @@ func Forget(
}

// Prune executes "restic prune"
func Prune(ctx context.Context) ([]byte, error) {
cmd := newCommand("prune", nil...)

func Prune(ctx context.Context, glob *GlobalOptions) ([]byte, error) {
cmd := cli.CommandType{
Binary: binary,
Command: "prune",
Args: cli.StructToCLI(glob),
}
return cli.Run(ctx, cmd)
}

// RebuildIndex executes "restic rebuild-index"
func RebuildIndex(ctx context.Context) ([]byte, error) {
func RebuildIndex(ctx context.Context, glob *GlobalOptions) ([]byte, error) {
nice := 19
ionice := 2
cmd := cli.CommandType{
Binary: binary,
Command: "rebuild-index",
Args: nil,
Args: cli.StructToCLI(glob),
Nice: &nice,
IONice: &ionice,
}
Expand Down Expand Up @@ -385,8 +398,11 @@ func Unlock(ctx context.Context, globalOpts *GlobalOptions, unlockOpts *UnlockOp
}

// Tag executes "restic tag"
func Tag(ctx context.Context, opts *TagOptions) ([]byte, error) {
cmd := newCommand("tag", cli.StructToCLI(opts)...)

func Tag(ctx context.Context, glob *GlobalOptions, opts *TagOptions) ([]byte, error) {
cmd := cli.CommandType{
Binary: binary,
Command: "tag",
Args: append(cli.StructToCLI(glob), cli.StructToCLI(opts)...),
}
return cli.Run(ctx, cmd)
}
Loading