Skip to content
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Besides creating backups, `brudi` can also be used to restore your data from bac
- [CLI](#cli)
- [Docker](#docker)
- [Configuration](#configuration)
- [Sources](#sources)
- [Sources](#sources)
- [FsBackup](#fsbackup)
- [Tar](#tar)
- [MySQLDump](#mysqldump)
- [MongoDump](#mongodump)
Expand All @@ -29,6 +30,7 @@ Besides creating backups, `brudi` can also be used to restore your data from bac
- [Sensitive data: Environment variables](#sensitive-data-environment-variables)
- [Gzip support for binaries without native gzip support](#gzip-support-for-binaries-without-native-gzip-support)
- [Restoring from backup](#restoring-from-backup)
- [FsRestore](#fsrestore)
- [TarRestore](#tarrestore)
- [MongoRestore](#mongorestore)
- [MySQLRestore](#mysqlrestore)
Expand Down Expand Up @@ -65,6 +67,8 @@ Usage:

Available Commands:
help Help about any command
fsbackup Backs up directories directly. Use it with the --restic flag.
fsrestore Restores directories directly. Use it with the --restic flag.
mongodump Creates a mongodump of your desired server
mongorestore Restores a server from a mongodump
mysqldump Creates a mysqldump of your desired server
Expand Down Expand Up @@ -127,6 +131,19 @@ In case the same config file has been provided more than once, only the first in

#### Sources

##### FsBackup

```yaml
fsbackup:
options:
path: /srv/data
hostName: autoGeneratedIfEmpty
```

Running: `brudi fsbackup -c ${HOME}/.brudi.yml --restic`

The `fsbackup` command validates that the configured directory exists and then hands it over to `restic backup` without creating intermediate archives.

##### Tar

```yaml
Expand Down Expand Up @@ -343,6 +360,24 @@ mysqlrestore:

#### Restoring from backup

##### FsRestore

```yaml
fsrestore:
options:
path: /srv/data
hostName: autoGeneratedIfEmpty
restic:
restore:
flags:
target: /restore-target
id: "latest"
```

Running: `brudi fsrestore -c ${HOME}/.brudi.yml --restic`

`fsrestore` triggers `restic restore` for the stored directory path without any additional processing. The configured `target` controls where the restored files are written.

##### TarRestore

```yaml
Expand Down Expand Up @@ -494,6 +529,7 @@ It is also possible to specify concrete snapshot-ids instead of `latest`.

### Source backup methods

- [x] `fsbackup`
- [x] `mysqldump`
- [x] `mongodump`
- [x] `tar`
Expand All @@ -502,6 +538,7 @@ It is also possible to specify concrete snapshot-ids instead of `latest`.

### Restore backup methods

- [x] `fsrestore`
- [x] `mysqlrestore`
- [x] `mongorestore`
- [x] `tarrestore`
Expand Down
30 changes: 30 additions & 0 deletions cmd/fsbackup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cmd

import (
"context"

"github.com/mittwald/brudi/pkg/source"
"github.com/mittwald/brudi/pkg/source/fsbackup"

"github.com/spf13/cobra"
)

var (
fsbackupCmd = &cobra.Command{
Use: "fsbackup",
Short: "Backs up directories directly with restic",
Long: "Backs up configured directories using restic without creating intermediate archives.",
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

if err := source.DoBackupForKind(ctx, fsbackup.Kind, cleanup, useRestic, useResticForget, useResticPrune); err != nil {
panic(err)
}
},
}
)

func init() {
rootCmd.AddCommand(fsbackupCmd)
}
30 changes: 30 additions & 0 deletions cmd/fsrestore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cmd

import (
"context"

"github.com/mittwald/brudi/pkg/source"
"github.com/mittwald/brudi/pkg/source/fsrestore"

"github.com/spf13/cobra"
)

var (
fsrestoreCmd = &cobra.Command{
Use: "fsrestore",
Short: "Restores directories directly from restic",
Long: "Restores directories from restic snapshots without additional processing.",
Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

if err := source.DoRestoreForKind(ctx, fsrestore.Kind, cleanup, useRestic); err != nil {
panic(err)
}
},
}
)

func init() {
rootCmd.AddCommand(fsrestoreCmd)
}
3 changes: 3 additions & 0 deletions pkg/source/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

log "github.com/sirupsen/logrus"

"github.com/mittwald/brudi/pkg/source/fsbackup"
"github.com/mittwald/brudi/pkg/source/mongodump"
"github.com/mittwald/brudi/pkg/source/mysqldump"
"github.com/mittwald/brudi/pkg/source/redisdump"
Expand All @@ -29,6 +30,8 @@ func getGenericBackendForKind(kind string) (Generic, error) {
return redisdump.NewConfigBasedBackend()
case tar.Kind:
return tar.NewConfigBasedBackend()
case fsbackup.Kind:
return fsbackup.NewConfigBasedBackend()
default:
return nil, fmt.Errorf("unsupported kind '%s'", kind)
}
Expand Down
56 changes: 56 additions & 0 deletions pkg/source/fsbackup/backend_config_based.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package fsbackup

import (
"context"
"fmt"
"os"

"github.com/pkg/errors"
)

type Options struct {
Path string `validate:"min=1"`
}

type ConfigBasedBackend struct {
cfg *Config
}

func NewConfigBasedBackend() (*ConfigBasedBackend, error) {
config := &Config{
Options: &Options{},
}

if err := config.InitFromViper(); err != nil {
return nil, err
}

return &ConfigBasedBackend{cfg: config}, nil
}

func (b *ConfigBasedBackend) CreateBackup(_ context.Context) error {
path := b.cfg.Options.Path

info, err := os.Stat(path)
if err != nil {
return errors.WithStack(err)
}

if !info.IsDir() {
return errors.WithStack(fmt.Errorf("configured path %s is not a directory", path))
}

return nil
}

func (b *ConfigBasedBackend) GetBackupPath() string {
return b.cfg.Options.Path
}

func (b *ConfigBasedBackend) GetHostname() string {
return b.cfg.HostName
}

func (b *ConfigBasedBackend) CleanUp() error {
return nil
}
39 changes: 39 additions & 0 deletions pkg/source/fsrestore/backend_config_based.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package fsrestore

import "context"

type Options struct {
Path string `validate:"min=1"`
}

type ConfigBasedBackend struct {
cfg *Config
}

func NewConfigBasedBackend() (*ConfigBasedBackend, error) {
config := &Config{
Options: &Options{},
}

if err := config.InitFromViper(); err != nil {
return nil, err
}

return &ConfigBasedBackend{cfg: config}, nil
}

func (b *ConfigBasedBackend) RestoreBackup(context.Context) error {
return nil
}

func (b *ConfigBasedBackend) GetBackupPath() string {
return b.cfg.Options.Path
}

func (b *ConfigBasedBackend) GetHostname() string {
return b.cfg.HostName
}

func (b *ConfigBasedBackend) CleanUp() error {
return nil
}
35 changes: 35 additions & 0 deletions pkg/source/fsrestore/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package fsrestore

import (
"os"

"github.com/pkg/errors"

"github.com/mittwald/brudi/pkg/config"
)

const (
// Kind identifies the fsrestore backend in configuration.
Kind = "fsrestore"
)

type Config struct {
Options *Options
HostName string `validate:"min=1"`
}

func (c *Config) InitFromViper() error {
err := config.InitializeStructFromViper(Kind, c)
if err != nil {
return errors.WithStack(err)
}

if c.HostName == "" {
c.HostName, err = os.Hostname()
if err != nil {
return errors.WithStack(err)
}
}

return config.Validate(c)
}
3 changes: 3 additions & 0 deletions pkg/source/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus"

"github.com/mittwald/brudi/pkg/restic"
"github.com/mittwald/brudi/pkg/source/fsrestore"
"github.com/mittwald/brudi/pkg/source/mongorestore"
"github.com/mittwald/brudi/pkg/source/mysqlrestore"
"github.com/mittwald/brudi/pkg/source/pgrestore"
Expand All @@ -26,6 +27,8 @@ func getGenericRestoreBackendForKind(kind string) (GenericRestore, error) {
return tarrestore.NewConfigBasedBackend()
case psql.Kind:
return psql.NewConfigBasedBackend()
case fsrestore.Kind:
return fsrestore.NewConfigBasedBackend()
default:
return nil, fmt.Errorf("unsupported kind '%s'", kind)
}
Expand Down
Loading