Compare commits

..

2 Commits

Author SHA1 Message Date
rdelaage
812fed163e Merge 0c7da11f4d into 068121d722 2023-10-12 07:11:49 -07:00
Romain de Laage
0c7da11f4d Fix upgrade command
fix #191
2022-11-14 09:02:56 +01:00
25 changed files with 1958 additions and 2359 deletions

View File

@@ -3,7 +3,7 @@ name: Main
on:
push:
tags:
- 'v*.*.*'
- "v*.*.*"
jobs:
docker:
@@ -40,7 +40,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.21'
go-version: "^1.20"
- name: Build
run: go run build/build.go
- name: Release

View File

@@ -1,4 +1,4 @@
FROM golang:1.22-alpine as builder
FROM golang:1.20-alpine as builder
WORKDIR /app
COPY go.* .
@@ -6,8 +6,8 @@ RUN go mod download
COPY . .
RUN go build
FROM restic/restic:0.17.0
RUN apk add --no-cache rclone bash curl docker-cli
FROM restic/restic:0.16.0
RUN apk add --no-cache rclone bash
COPY --from=builder /app/autorestic /usr/bin/autorestic
ENTRYPOINT []
CMD [ "autorestic" ]

View File

@@ -34,7 +34,7 @@ Autorestic is a wrapper around the amazing [restic](https://restic.net/). While
- Backup locations to multiple backends
- Snapshot policies and pruning
- Fully encrypted
- Before/after backup hooks
- Pre/After hooks
- Exclude pattern/files
- Cron jobs for automatic backup
- Backup & Restore docker volume

View File

@@ -59,7 +59,24 @@ func initConfig() {
os.Exit(1)
}
} else {
configPaths := getConfigPaths()
configPaths := []string{"."}
// Home
if home, err := homedir.Dir(); err == nil {
configPaths = append(configPaths, home)
}
// XDG_CONFIG_HOME
{
prefix, found := os.LookupEnv("XDG_CONFIG_HOME")
if !found {
if home, err := homedir.Dir(); err != nil {
prefix = filepath.Join(home, ".config")
}
}
xdgConfig := filepath.Join(prefix, "autorestic")
configPaths = append(configPaths, xdgConfig)
}
for _, cfgPath := range configPaths {
viper.AddConfigPath(cfgPath)
}
@@ -71,22 +88,3 @@ func initConfig() {
viper.AutomaticEnv()
}
}
func getConfigPaths() []string {
result := []string{"."}
if home, err := homedir.Dir(); err == nil {
result = append(result, home)
}
{
xdgConfigHome, found := os.LookupEnv("XDG_CONFIG_HOME")
if !found {
if home, err := homedir.Dir(); err == nil {
xdgConfigHome = filepath.Join(home, ".config")
}
}
xdgConfig := filepath.Join(xdgConfigHome, "autorestic")
result = append(result, xdgConfig)
}
return result
}

View File

@@ -1,36 +0,0 @@
package cmd
import (
"github.com/mitchellh/go-homedir"
"os"
"path/filepath"
"slices"
"testing"
)
const xdgConfigHome = "XDG_CONFIG_HOME"
func assertContains(t *testing.T, array []string, element string) {
if !slices.Contains(array, element) {
t.Errorf("Expected %s to be contained in %s", element, array)
}
}
func TestConfigResolving(t *testing.T) {
t.Run("~/.config/autorestic is used if XDG_CONFIG_HOME is not set", func(t *testing.T) {
// Override env using testing so that env gets restored after test
t.Setenv(xdgConfigHome, "")
_ = os.Unsetenv("XDG_CONFIG_HOME")
configPaths := getConfigPaths()
homeDir, _ := homedir.Dir()
expectedConfigPath := filepath.Join(homeDir, ".config/autorestic")
assertContains(t, configPaths, expectedConfigPath)
})
t.Run("XDG_CONFIG_HOME is respected if set", func(t *testing.T) {
t.Setenv(xdgConfigHome, "/foo/bar")
configPaths := getConfigPaths()
assertContains(t, configPaths, filepath.Join("/", "foo", "bar", "autorestic"))
})
}

View File

@@ -1,81 +0,0 @@
package cmd
import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"
"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/spf13/cobra"
)
var unlockCmd = &cobra.Command{
Use: "unlock",
Short: "Unlock autorestic only if you are sure that no other instance is running",
Long: `Unlock autorestic only if you are sure that no other instance is running.
To check you can run "ps aux | grep autorestic".`,
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
force, _ := cmd.Flags().GetBool("force")
if !force && isAutoresticRunning() {
colors.Error.Print("Another autorestic instance is running. Are you sure you want to unlock? (yes/no): ")
var response string
fmt.Scanln(&response)
if strings.ToLower(response) != "yes" {
colors.Primary.Println("Unlocking aborted.")
return
}
}
err := lock.Unlock()
if err != nil {
colors.Error.Println("Could not unlock:", err)
return
}
colors.Success.Println("Unlock successful")
},
}
func init() {
rootCmd.AddCommand(unlockCmd)
unlockCmd.Flags().Bool("force", false, "force unlock")
}
// isAutoresticRunning checks if autorestic is running
// and returns true if it is.
// It also prints the processes to stdout.
func isAutoresticRunning() bool {
cmd := exec.Command("sh", "-c", "ps aux | grep autorestic")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return false
}
lines := strings.Split(out.String(), "\n")
autoresticProcesses := []string{}
currentPid := fmt.Sprint(os.Getpid())
for _, line := range lines {
if strings.Contains(line, "autorestic") && !strings.Contains(line, "grep autorestic") && !strings.Contains(line, currentPid) {
autoresticProcesses = append(autoresticProcesses, line)
}
}
if len(autoresticProcesses) > 0 {
colors.Faint.Println("Found autorestic processes:")
for _, proc := range autoresticProcesses {
colors.Faint.Println(proc)
}
return true
}
return false
}

View File

@@ -1 +1 @@
v22.7.0
v20.8.0

View File

@@ -4,11 +4,11 @@
"dev": "NEXT_TELEMETRY_DISABLED=1 next"
},
"dependencies": {
"next": "^14.2.7",
"nextra": "^2.13.4",
"nextra-theme-docs": "^2.13.4",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"next": "^13.5.3",
"nextra": "^2.13.1",
"nextra-theme-docs": "^2.13.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"packageManager": "pnpm@9.9.0"
"packageManager": "pnpm@8.8.0"
}

View File

@@ -16,25 +16,3 @@ backends:
## Types
We restic supports multiple types of backends. See the [full list](/backend/available) for details.
## Avoid Generating Keys
By default, `autorestic` will generate a key for every backend if none is defined. This is done by updating your config file with the key.
In cases where you want to provide the key yourself, you can ensure that `autorestic` doesn't accidentally generate one for you by setting `requireKey: true`.
Example:
```yaml | .autorestic.yml
version: 2
backends:
foo:
type: local
path: /data/my/backups
# Alternatively, you can set the key through the `AUTORESTIC_FOO_RESTIC_PASSWORD` environment variable.
key: ... your key here ...
requireKey: true
```
With this setting, if a key is missing, `autorestic` will crash instead of generating a new key and updating your config file.

View File

@@ -4,7 +4,7 @@
autorestic forget [-l, --location] [-a, --all] [--dry-run] [--prune]
```
This will prune and remove old data form the backends according to the [keep policy you have specified for the location](/location/options/forget).
This will prune and remove old data form the backends according to the [keep policy you have specified for the location](/location/forget).
The `--dry-run` flag will do a dry run showing what would have been deleted, but won't touch the actual data.

View File

@@ -1,32 +0,0 @@
# Unlock
In case autorestic throws the error message `an instance is already running. exiting`, but there is no instance running you can unlock the lock.
To verify that there is no instance running you can use `ps aux | grep autorestic`.
Example with no instance running:
```bash
> ps aux | grep autorestic
root 39260 0.0 0.0 6976 2696 pts/11 S+ 19:41 0:00 grep autorestic
```
Example with an instance running:
```bash
> ps aux | grep autorestic
root 29465 0.0 0.0 1162068 7380 pts/7 Sl+ 19:28 0:00 autorestic --ci backup -a
root 39260 0.0 0.0 6976 2696 pts/11 S+ 19:41 0:00 grep autorestic
```
**If an instance is running you should not unlock as it could lead to data loss!**
```bash
autorestic unlock
```
Use the `--force` to prevent the confirmation prompt if an instance is running.
```bash
autorestic unlock --force
```

View File

@@ -1,7 +1,5 @@
# 🏘 Community
## Software
A list of community driven projects. (No official affiliation)
- SystemD Units: <https://gitlab.com/py_crash/autorestic-systemd-units>
@@ -11,7 +9,3 @@ A list of community driven projects. (No official affiliation)
- Ansible Role: <https://github.com/FuzzyMistborn/ansible-role-autorestic>
- Ansible Role: <https://0xacab.org/varac-projects/ansible-role-autorestic>
- Ansible Role: <https://github.com/dbrennand/ansible-role-autorestic>
## Writing
- [restic: excellent resource for local and cloud backup](https://notes.nicfab.eu/en/posts/restic/)

View File

@@ -2,11 +2,10 @@
## Path
By default autorestic searches for a `.autorestic.yml` file in the current directory, your home folder and your XDG config folder (`~/.config/` by default):
By default autorestic searches for a `.autorestic.yml` file in the current directory and your home folder.
- `./.autorestic.yml`
- `~/.autorestic.yml`
- `~/.config/autorestic/.autorestic.yml`
You can also specify a custom file with the `-c path/to/some/config.yml`
@@ -56,8 +55,6 @@ version: 2
extras:
hooks: &foo
prevalidate:
- echo "Wake up!"
before:
- echo "Hello"
after:

View File

@@ -13,7 +13,7 @@ Autorestic is a wrapper around the amazing [restic](https://restic.net/). While
- Backup locations to multiple backends
- Snapshot policies and pruning
- Fully encrypted
- Before/after backup hooks
- Pre/After hooks
- Exclude pattern/files
- Cron jobs for automatic backup
- Backup & Restore docker volumes

View File

@@ -30,4 +30,4 @@ Fedora users can install the [autorestic](https://src.fedoraproject.org/rpms/aut
### AUR
If you are on Arch there is an [AUR Package](https://aur.archlinux.org/packages/autorestic-bin/)
~~If you are on Arch there is an [AUR Package](https://aur.archlinux.org/packages/autorestic-bin/) (looking for maintainers).~~ - Deprecated

View File

@@ -6,28 +6,23 @@ They consist of a list of commands that will be executed in the same directory a
The following hooks groups are supported, none are required:
- `prevalidate`
- `before`
- `after`
- `failure`
- `success`
The difference between `prevalidate` and `before` hooks are that `prevalidate` is run before checking the backup location is valid, including checking that the `from` directories exist. This can be useful, for example, to mount the source filesystem that contains the directories listed in `from`.
```yml | .autorestic.yml
locations:
my-location:
from: /data
to: my-backend
hooks:
prevalidate:
- echo "Checks"
before:
- echo "One"
- echo "Two"
- echo "Three"
after:
- echo "Bye"
- echo "Byte"
failure:
- echo "Something went wrong"
success:
@@ -36,15 +31,13 @@ locations:
## Flowchart
1. `prevalidate` hook
2. Check backup location
3. `before` hook
4. Run backup
5. `after` hook
6. - `success` hook if no errors were found
1. `before` hook
2. Run backup
3. `after` hook
4. - `success` hook if no errors were found
- `failure` hook if at least one error was encountered
If either the `prevalidate` or `before` hook encounters errors then the backup and `after` hooks will be skipped and only the `failed` hooks will run.
If the `before` hook encounters errors the backup and `after` hooks will be skipped and only the `failed` hooks will run.
## Environment variables

3926
docs/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

7
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/cupcakearmy/autorestic
go 1.21
go 1.20
require (
github.com/blang/semver/v4 v4.0.0
@@ -11,11 +11,9 @@ require (
github.com/robfig/cron v1.2.0
github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.11.0
github.com/stretchr/testify v1.9.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
@@ -25,7 +23,6 @@ require (
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
@@ -35,5 +32,5 @@ require (
golang.org/x/text v0.3.8 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

7
go.sum
View File

@@ -182,9 +182,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -488,8 +487,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -10,8 +10,6 @@ if [[ $NATIVE_OS == *"linux"* ]]; then
OS=linux
elif [[ $NATIVE_OS == *"darwin"* ]]; then
OS=darwin
elif [[ $NATIVE_OS == *"freebsd"* ]]; then
OS=freebsd
else
echo "Could not determine OS automatically, please check the release page manually: https://github.com/cupcakearmy/autorestic/releases"
exit 1
@@ -19,7 +17,7 @@ fi
echo $OS
NATIVE_ARCH=$(uname -m | tr '[:upper:]' '[:lower:]')
if [[ $NATIVE_ARCH == *"x86_64"* || $NATIVE_ARCH == *"amd64"* ]]; then
if [[ $NATIVE_ARCH == *"x86_64"* ]]; then
ARCH=amd64
elif [[ $NATIVE_ARCH == *"arm64"* || $NATIVE_ARCH == *"aarch64"* ]]; then
ARCH=arm64

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"net/url"
"os"
"regexp"
"strings"
"github.com/cupcakearmy/autorestic/internal/colors"
@@ -19,14 +18,13 @@ type BackendRest struct {
}
type Backend struct {
name string
Type string `mapstructure:"type,omitempty"`
Path string `mapstructure:"path,omitempty"`
Key string `mapstructure:"key,omitempty"`
RequireKey bool `mapstructure:"requireKey,omitempty"`
Env map[string]string `mapstructure:"env,omitempty"`
Rest BackendRest `mapstructure:"rest,omitempty"`
Options Options `mapstructure:"options,omitempty"`
name string
Type string `mapstructure:"type,omitempty"`
Path string `mapstructure:"path,omitempty"`
Key string `mapstructure:"key,omitempty"`
Env map[string]string `mapstructure:"env,omitempty"`
Rest BackendRest `mapstructure:"rest,omitempty"`
Options Options `mapstructure:"options,omitempty"`
}
func GetBackend(name string) (Backend, bool) {
@@ -59,8 +57,6 @@ func (b Backend) generateRepo() (string, error) {
}
}
var nonAlphaRegex = regexp.MustCompile("[^A-Za-z0-9]")
func (b Backend) getEnv() (map[string]string, error) {
env := make(map[string]string)
// Key
@@ -76,9 +72,7 @@ func (b Backend) getEnv() (map[string]string, error) {
}
// From Envfile and passed as env
nameForEnv := strings.ToUpper(b.name)
nameForEnv = nonAlphaRegex.ReplaceAllString(nameForEnv, "_")
var prefix = "AUTORESTIC_" + nameForEnv + "_"
var prefix = "AUTORESTIC_" + strings.ToUpper(b.name) + "_"
for _, variable := range os.Environ() {
var splitted = strings.SplitN(variable, "=", 2)
if strings.HasPrefix(splitted[0], prefix) {
@@ -110,9 +104,6 @@ func (b Backend) validate() error {
// Check if key is set in environment
env, _ := b.getEnv()
if _, found := env["RESTIC_PASSWORD"]; !found {
if b.RequireKey {
return fmt.Errorf("backend %s requires a key but none was provided", b.name)
}
// No key set in config file or env => generate random key and save file
key := generateRandomKey()
b.Key = key
@@ -152,7 +143,6 @@ func (b Backend) Exec(args []string) error {
return err
}
options := ExecuteOptions{Envs: env}
args = append(args, combineBackendOptions("exec", b)...)
_, out, err := ExecuteResticCommand(options, args...)
if err != nil {
colors.Error.Println(out)
@@ -192,7 +182,6 @@ func (b Backend) ExecDocker(l Location, args []string) (int, string, error) {
case "s3":
case "azure":
case "gs":
case "rest":
// No additional setup needed
case "rclone":
// Read host rclone config and mount it into the container

View File

@@ -6,7 +6,6 @@ import (
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)
func TestGenerateRepo(t *testing.T) {
@@ -195,33 +194,6 @@ func TestGetEnv(t *testing.T) {
assertEqual(t, result["B2_ACCOUNT_ID"], "foo123")
assertEqual(t, result["B2_ACCOUNT_KEY"], "foo456")
})
for _, char := range "@-_:/" {
t.Run(fmt.Sprintf("env var with special char (%c)", char), func(t *testing.T) {
// generate env variables
// TODO better way to teardown
defer os.Unsetenv("AUTORESTIC_FOO_BAR_RESTIC_PASSWORD")
defer os.Unsetenv("AUTORESTIC_FOO_BAR_B2_ACCOUNT_ID")
defer os.Unsetenv("AUTORESTIC_FOO_BAR_B2_ACCOUNT_KEY")
os.Setenv("AUTORESTIC_FOO_BAR_RESTIC_PASSWORD", "secret123")
os.Setenv("AUTORESTIC_FOO_BAR_B2_ACCOUNT_ID", "foo123")
os.Setenv("AUTORESTIC_FOO_BAR_B2_ACCOUNT_KEY", "foo456")
b := Backend{
name: fmt.Sprintf("foo%cbar", char),
Type: "local",
Path: "/foo/bar",
}
result, err := b.getEnv()
if err != nil {
t.Errorf("unexpected error %v", err)
}
assertEqual(t, result["RESTIC_REPOSITORY"], "/foo/bar")
assertEqual(t, result["RESTIC_PASSWORD"], "secret123")
assertEqual(t, result["B2_ACCOUNT_ID"], "foo123")
assertEqual(t, result["B2_ACCOUNT_KEY"], "foo456")
})
}
}
func TestValidate(t *testing.T) {
@@ -250,16 +222,4 @@ func TestValidate(t *testing.T) {
}
assertEqual(t, err.Error(), "Backend \"foo\" has no \"path\"")
})
t.Run("require key with no key", func(t *testing.T) {
b := Backend{
name: "foo",
Type: "local",
Path: "~/foo/bar",
RequireKey: true,
}
err := b.validate()
fmt.Printf("error: %v\n", err)
assert.EqualError(t, err, "backend foo requires a key but none was provided")
})
}

View File

@@ -17,7 +17,7 @@ import (
"github.com/spf13/viper"
)
const VERSION = "1.8.3"
const VERSION = "1.7.9"
type OptionMap map[string][]interface{}
type Options map[string]OptionMap
@@ -132,11 +132,10 @@ func (c *Config) Describe() {
tmp = ""
hooks := map[string][]string{
"PreValidate": l.Hooks.PreValidate,
"Before": l.Hooks.Before,
"After": l.Hooks.After,
"Failure": l.Hooks.Failure,
"Success": l.Hooks.Success,
"Before": l.Hooks.Before,
"After": l.Hooks.After,
"Failure": l.Hooks.Failure,
"Success": l.Hooks.Success,
}
for hook, commands := range hooks {
if len(commands) > 0 {

View File

@@ -33,12 +33,11 @@ const (
)
type Hooks struct {
Dir string `mapstructure:"dir"`
PreValidate HookArray `mapstructure:"prevalidate,omitempty"`
Before HookArray `mapstructure:"before,omitempty"`
After HookArray `mapstructure:"after,omitempty"`
Success HookArray `mapstructure:"success,omitempty"`
Failure HookArray `mapstructure:"failure,omitempty"`
Dir string `mapstructure:"dir"`
Before HookArray `mapstructure:"before,omitempty"`
After HookArray `mapstructure:"after,omitempty"`
Success HookArray `mapstructure:"success,omitempty"`
Failure HookArray `mapstructure:"failure,omitempty"`
}
type LocationCopy = map[string][]string
@@ -185,18 +184,12 @@ func (l Location) Backup(cron bool, specificBackend string) []error {
},
}
// Hooks before location validation
if err := l.ExecuteHooks(l.Hooks.PreValidate, options); err != nil {
errors = append(errors, err)
goto after
}
if err := l.validate(); err != nil {
errors = append(errors, err)
goto after
}
// Hooks after location validation
// Hooks
if err := l.ExecuteHooks(l.Hooks.Before, options); err != nil {
errors = append(errors, err)
goto after
@@ -296,13 +289,12 @@ func (l Location) Backup(cron bool, specificBackend string) []error {
}
}
// After backup hooks
// After hooks
if err := l.ExecuteHooks(l.Hooks.After, options); err != nil {
errors = append(errors, err)
}
after:
// Success/failure hooks
var commands []string
var isSuccess = len(errors) == 0
if isSuccess {

View File

@@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,