mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2024-12-23 08:46:25 +00:00
Merge f43cc32ac3
into 3b57602fe8
This commit is contained in:
commit
f7a20a1210
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/cupcakearmy/autorestic/internal"
|
"github.com/cupcakearmy/autorestic/internal"
|
||||||
|
"github.com/cupcakearmy/autorestic/internal/colors"
|
||||||
"github.com/cupcakearmy/autorestic/internal/lock"
|
"github.com/cupcakearmy/autorestic/internal/lock"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -42,8 +43,13 @@ var restoreCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = l.Restore(target, from, force, snapshot, optional)
|
errs := l.Restore(target, from, force, snapshot, optional)
|
||||||
CheckErr(err)
|
for _, err := range errs {
|
||||||
|
colors.Error.Printf("%s\n\n", err)
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
CheckErr(fmt.Errorf("%d errors were found", len(errs)))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
>
|
>
|
||||||
> [0.x → 1.0](/migration/0.x_1.0)
|
> [0.x → 1.0](/migration/0.x_1.0)
|
||||||
> [1.4 → 1.5](/migration/1.4_1.5)
|
> [1.4 → 1.5](/migration/1.4_1.5)
|
||||||
|
> [1.7 → 1.8](/migration/1.7_1.8)
|
||||||
|
|
||||||
[Examples](/examples)
|
[Examples](/examples)
|
||||||
[Docker](/docker)
|
[Docker](/docker)
|
||||||
|
@ -55,6 +55,7 @@ version: 2
|
|||||||
|
|
||||||
extras:
|
extras:
|
||||||
hooks: &foo
|
hooks: &foo
|
||||||
|
backup:
|
||||||
before:
|
before:
|
||||||
- echo "Hello"
|
- echo "Hello"
|
||||||
after:
|
after:
|
||||||
|
@ -22,6 +22,7 @@ autorestic exec -b my-backend -- unlock
|
|||||||
extras:
|
extras:
|
||||||
healthchecks: &healthchecks
|
healthchecks: &healthchecks
|
||||||
hooks:
|
hooks:
|
||||||
|
backup:
|
||||||
before:
|
before:
|
||||||
- 'curl -m 10 --retry 5 -X POST -H "Content-Type: text/plain" --data "Starting backup for location: ${AUTORESTIC_LOCATION}" https://<healthchecks-url>/ping/<uid>/start'
|
- 'curl -m 10 --retry 5 -X POST -H "Content-Type: text/plain" --data "Starting backup for location: ${AUTORESTIC_LOCATION}" https://<healthchecks-url>/ping/<uid>/start'
|
||||||
failure:
|
failure:
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# Hooks
|
# Hooks
|
||||||
|
|
||||||
If you want to perform some commands before and/or after a backup, you can use hooks.
|
If you want to perform some commands before and/or after a backup or restore, you can use hooks.
|
||||||
|
|
||||||
They consist of a list of commands that will be executed in the same directory as the target `from`.
|
They consist of a list of commands that will be executed in the same directory as the config file or in the `dir` directory if configured.
|
||||||
|
|
||||||
The following hooks groups are supported, none are required:
|
The following hooks groups are supported, none are required:
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ locations:
|
|||||||
from: /data
|
from: /data
|
||||||
to: my-backend
|
to: my-backend
|
||||||
hooks:
|
hooks:
|
||||||
|
backup:
|
||||||
before:
|
before:
|
||||||
- echo "One"
|
- echo "One"
|
||||||
- echo "Two"
|
- echo "Two"
|
||||||
@ -27,6 +28,16 @@ locations:
|
|||||||
- echo "Something went wrong"
|
- echo "Something went wrong"
|
||||||
success:
|
success:
|
||||||
- echo "Well done!"
|
- echo "Well done!"
|
||||||
|
restore:
|
||||||
|
dir: /var/www/html
|
||||||
|
before:
|
||||||
|
- echo "Let's restore this backup!"
|
||||||
|
after:
|
||||||
|
- echo "Finished to restore"
|
||||||
|
failure:
|
||||||
|
- echo "A problem has been encountered :("
|
||||||
|
success:
|
||||||
|
- echo "Successfully restored!"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Flowchart
|
## Flowchart
|
||||||
|
45
docs/markdown/migration/1.7_1.8.md
Normal file
45
docs/markdown/migration/1.7_1.8.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Migration from `1.7` to `1.8`
|
||||||
|
|
||||||
|
## Config files
|
||||||
|
|
||||||
|
- The config version have been changed
|
||||||
|
- You can now configure restore hooks
|
||||||
|
|
||||||
|
See detailed instructions below.
|
||||||
|
|
||||||
|
## Config Version
|
||||||
|
|
||||||
|
The version field of the config file has been changed from `2` to `3`.
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
Since `1.8` both backup and restore hooks are possible.
|
||||||
|
For this reason, backup hooks have been moved one layer deeper, you have to move them in a `backup` object.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
locations:
|
||||||
|
l1:
|
||||||
|
# ...
|
||||||
|
from: /foo/bar
|
||||||
|
hooks:
|
||||||
|
before:
|
||||||
|
- pwd
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
locations:
|
||||||
|
l1:
|
||||||
|
# ...
|
||||||
|
from: /foo/bar
|
||||||
|
hooks:
|
||||||
|
backup:
|
||||||
|
before:
|
||||||
|
- pwd
|
||||||
|
restore:
|
||||||
|
after:
|
||||||
|
- echo "My super restore hook"
|
||||||
|
```
|
@ -2,3 +2,4 @@
|
|||||||
|
|
||||||
- [From 0.x to 1.0](/migration/0.x_1.0)
|
- [From 0.x to 1.0](/migration/0.x_1.0)
|
||||||
- [From 1.4 to 1.5](/migration/1.4_1.5)
|
- [From 1.4 to 1.5](/migration/1.4_1.5)
|
||||||
|
- [From 1.7 to 1.8](/migration/1.7_1.8)
|
||||||
|
@ -83,7 +83,7 @@ func GetConfig() *Config {
|
|||||||
exitConfig(nil, "version specified in config file is not an int")
|
exitConfig(nil, "version specified in config file is not an int")
|
||||||
} else {
|
} else {
|
||||||
// Check for version
|
// Check for version
|
||||||
if version != 2 {
|
if version != 3 {
|
||||||
exitConfig(nil, "unsupported config version number. please check the docs for migration\nhttps://autorestic.vercel.app/migration/")
|
exitConfig(nil, "unsupported config version number. please check the docs for migration\nhttps://autorestic.vercel.app/migration/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,10 +132,14 @@ func (c *Config) Describe() {
|
|||||||
|
|
||||||
tmp = ""
|
tmp = ""
|
||||||
hooks := map[string][]string{
|
hooks := map[string][]string{
|
||||||
"Before": l.Hooks.Before,
|
"Before backup": l.Hooks.BackupOption.Before,
|
||||||
"After": l.Hooks.After,
|
"After backup": l.Hooks.BackupOption.After,
|
||||||
"Failure": l.Hooks.Failure,
|
"Failure backup": l.Hooks.BackupOption.Failure,
|
||||||
"Success": l.Hooks.Success,
|
"Success backup": l.Hooks.BackupOption.Success,
|
||||||
|
"Before restore": l.Hooks.RestoreOption.Before,
|
||||||
|
"After restore": l.Hooks.RestoreOption.After,
|
||||||
|
"Failure restore": l.Hooks.RestoreOption.Failure,
|
||||||
|
"Success restore": l.Hooks.RestoreOption.Success,
|
||||||
}
|
}
|
||||||
for hook, commands := range hooks {
|
for hook, commands := range hooks {
|
||||||
if len(commands) > 0 {
|
if len(commands) > 0 {
|
||||||
|
@ -33,6 +33,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Hooks struct {
|
type Hooks struct {
|
||||||
|
RestoreOption HooksList `mapstructure:"restore,omitempty"`
|
||||||
|
BackupOption HooksList `mapstructure:"backup,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocationCopy = map[string][]string
|
||||||
|
|
||||||
|
type HooksList struct {
|
||||||
Dir string `mapstructure:"dir"`
|
Dir string `mapstructure:"dir"`
|
||||||
Before HookArray `mapstructure:"before,omitempty"`
|
Before HookArray `mapstructure:"before,omitempty"`
|
||||||
After HookArray `mapstructure:"after,omitempty"`
|
After HookArray `mapstructure:"after,omitempty"`
|
||||||
@ -40,8 +47,6 @@ type Hooks struct {
|
|||||||
Failure HookArray `mapstructure:"failure,omitempty"`
|
Failure HookArray `mapstructure:"failure,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocationCopy = map[string][]string
|
|
||||||
|
|
||||||
type Location struct {
|
type Location struct {
|
||||||
name string `mapstructure:",omitempty"`
|
name string `mapstructure:",omitempty"`
|
||||||
From []string `mapstructure:"from,omitempty"`
|
From []string `mapstructure:"from,omitempty"`
|
||||||
@ -123,12 +128,12 @@ func (l Location) validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) ExecuteHooks(commands []string, options ExecuteOptions) error {
|
func (l Location) ExecuteHooks(commands []string, directory string, options ExecuteOptions) error {
|
||||||
if len(commands) == 0 {
|
if len(commands) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if l.Hooks.Dir != "" {
|
if directory != "" {
|
||||||
if dir, err := GetPathRelativeToConfig(l.Hooks.Dir); err != nil {
|
if dir, err := GetPathRelativeToConfig(directory); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
options.Dir = dir
|
options.Dir = dir
|
||||||
@ -190,7 +195,7 @@ func (l Location) Backup(cron bool, specificBackend string) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
if err := l.ExecuteHooks(l.Hooks.Before, options); err != nil {
|
if err := l.ExecuteHooks(l.Hooks.BackupOption.Before, l.Hooks.BackupOption.Dir, options); err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
goto after
|
goto after
|
||||||
}
|
}
|
||||||
@ -290,7 +295,7 @@ func (l Location) Backup(cron bool, specificBackend string) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// After hooks
|
// After hooks
|
||||||
if err := l.ExecuteHooks(l.Hooks.After, options); err != nil {
|
if err := l.ExecuteHooks(l.Hooks.BackupOption.After, l.Hooks.BackupOption.Dir, options); err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,11 +303,11 @@ after:
|
|||||||
var commands []string
|
var commands []string
|
||||||
var isSuccess = len(errors) == 0
|
var isSuccess = len(errors) == 0
|
||||||
if isSuccess {
|
if isSuccess {
|
||||||
commands = l.Hooks.Success
|
commands = l.Hooks.BackupOption.Success
|
||||||
} else {
|
} else {
|
||||||
commands = l.Hooks.Failure
|
commands = l.Hooks.BackupOption.Failure
|
||||||
}
|
}
|
||||||
if err := l.ExecuteHooks(commands, options); err != nil {
|
if err := l.ExecuteHooks(commands, l.Hooks.BackupOption.Dir, options); err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,11 +375,35 @@ func buildRestoreCommand(l Location, to string, snapshot string, options []strin
|
|||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Restore(to, from string, force bool, snapshot string, options []string) error {
|
func (l Location) Restore(to, from string, force bool, snapshot string, options []string) (errors []error) {
|
||||||
|
cwd, _ := GetPathRelativeToConfig(".")
|
||||||
|
hooksOptions := ExecuteOptions{
|
||||||
|
Command: "bash",
|
||||||
|
Dir: cwd,
|
||||||
|
Envs: map[string]string{
|
||||||
|
"AUTORESTIC_LOCATION": l.name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
var commands []string
|
||||||
|
var isSuccess = len(errors) == 0
|
||||||
|
if isSuccess {
|
||||||
|
commands = l.Hooks.RestoreOption.Success
|
||||||
|
} else {
|
||||||
|
commands = l.Hooks.RestoreOption.Failure
|
||||||
|
}
|
||||||
|
if err := l.ExecuteHooks(commands, l.Hooks.RestoreOption.Dir, hooksOptions); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
colors.Success.Println("Done")
|
||||||
|
}()
|
||||||
|
|
||||||
if from == "" {
|
if from == "" {
|
||||||
from = l.To[0]
|
from = l.To[0]
|
||||||
} else if !l.hasBackend(from) {
|
} else if !l.hasBackend(from) {
|
||||||
return fmt.Errorf("invalid backend: \"%s\"", from)
|
errors = append(errors, fmt.Errorf("invalid backend: \"%s\"", from))
|
||||||
}
|
}
|
||||||
|
|
||||||
if snapshot == "" {
|
if snapshot == "" {
|
||||||
@ -385,15 +414,23 @@ func (l Location) Restore(to, from string, force bool, snapshot string, options
|
|||||||
backend, _ := GetBackend(from)
|
backend, _ := GetBackend(from)
|
||||||
colors.Secondary.Printf("Restoring %s@%s → %s\n", snapshot, backend.name, to)
|
colors.Secondary.Printf("Restoring %s@%s → %s\n", snapshot, backend.name, to)
|
||||||
|
|
||||||
|
// Before Hooks for restore
|
||||||
|
if err := l.ExecuteHooks(l.Hooks.RestoreOption.Before, l.Hooks.RestoreOption.Dir, hooksOptions); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t, err := l.getType()
|
t, err := l.getType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
errors = append(errors, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
switch t {
|
switch t {
|
||||||
case TypeLocal:
|
case TypeLocal:
|
||||||
to, err = filepath.Abs(to)
|
to, err = filepath.Abs(to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
errors = append(errors, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Check if target is empty
|
// Check if target is empty
|
||||||
if !force {
|
if !force {
|
||||||
@ -402,14 +439,17 @@ func (l Location) Restore(to, from string, force bool, snapshot string, options
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
files, err := ioutil.ReadDir(to)
|
files, err := ioutil.ReadDir(to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
errors = append(errors, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if len(files) > 0 {
|
if len(files) > 0 {
|
||||||
return notEmptyError
|
errors = append(errors, notEmptyError)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
return err
|
errors = append(errors, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,10 +458,17 @@ func (l Location) Restore(to, from string, force bool, snapshot string, options
|
|||||||
_, _, err = backend.ExecDocker(l, buildRestoreCommand(l, "/", snapshot, options))
|
_, _, err = backend.ExecDocker(l, buildRestoreCommand(l, "/", snapshot, options))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
errors = append(errors, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
colors.Success.Println("Done")
|
|
||||||
return nil
|
// After Hooks for restore
|
||||||
|
if err := l.ExecuteHooks(l.Hooks.RestoreOption.After, l.Hooks.RestoreOption.Dir, hooksOptions); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) RunCron() error {
|
func (l Location) RunCron() error {
|
||||||
|
Loading…
Reference in New Issue
Block a user