Compare commits

..

4 Commits

Author SHA1 Message Date
Florian
ca66c4934e Merge 8b60120342 into b5604b8b9f 2024-02-09 16:06:33 -07:00
rwxd
8b60120342 fix(cmd unlock): add force flag 2023-10-10 21:23:17 +02:00
rwxd
ba51d1d062 fix(unlock cmd): get user confirmation in case an instance is still running 2023-10-10 20:56:55 +02:00
rwxd
43efd1db1a fix: cli command to unlock the autorestic running value 2023-10-10 20:45:48 +02:00
13 changed files with 50 additions and 74 deletions

View File

@@ -1,4 +1,4 @@
FROM golang:1.22-alpine as builder FROM golang:1.21-alpine as builder
WORKDIR /app WORKDIR /app
COPY go.* . COPY go.* .
@@ -7,7 +7,7 @@ COPY . .
RUN go build RUN go build
FROM restic/restic:0.16.4 FROM restic/restic:0.16.4
RUN apk add --no-cache rclone bash curl docker-cli RUN apk add --no-cache rclone bash curl
COPY --from=builder /app/autorestic /usr/bin/autorestic COPY --from=builder /app/autorestic /usr/bin/autorestic
ENTRYPOINT [] ENTRYPOINT []
CMD [ "autorestic" ] 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 - Backup locations to multiple backends
- Snapshot policies and pruning - Snapshot policies and pruning
- Fully encrypted - Fully encrypted
- Before/after backup hooks - Pre/After hooks
- Exclude pattern/files - Exclude pattern/files
- Cron jobs for automatic backup - Cron jobs for automatic backup
- Backup & Restore docker volume - Backup & Restore docker volume

View File

@@ -3,7 +3,6 @@ package cmd
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"os"
"os/exec" "os/exec"
"strings" "strings"
@@ -62,10 +61,9 @@ func isAutoresticRunning() bool {
lines := strings.Split(out.String(), "\n") lines := strings.Split(out.String(), "\n")
autoresticProcesses := []string{} autoresticProcesses := []string{}
currentPid := fmt.Sprint(os.Getpid())
for _, line := range lines { for _, line := range lines {
if strings.Contains(line, "autorestic") && !strings.Contains(line, "grep autorestic") && !strings.Contains(line, currentPid) { if strings.Contains(line, "autorestic") && !strings.Contains(line, "grep autorestic") {
autoresticProcesses = append(autoresticProcesses, line) autoresticProcesses = append(autoresticProcesses, line)
} }
} }

View File

@@ -56,8 +56,6 @@ version: 2
extras: extras:
hooks: &foo hooks: &foo
prevalidate:
- echo "Wake up!"
before: before:
- echo "Hello" - echo "Hello"
after: after:

View File

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

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: The following hooks groups are supported, none are required:
- `prevalidate`
- `before` - `before`
- `after` - `after`
- `failure` - `failure`
- `success` - `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 ```yml | .autorestic.yml
locations: locations:
my-location: my-location:
from: /data from: /data
to: my-backend to: my-backend
hooks: hooks:
prevalidate:
- echo "Checks"
before: before:
- echo "One" - echo "One"
- echo "Two" - echo "Two"
- echo "Three" - echo "Three"
after: after:
- echo "Bye" - echo "Byte"
failure: failure:
- echo "Something went wrong" - echo "Something went wrong"
success: success:
@@ -36,15 +31,13 @@ locations:
## Flowchart ## Flowchart
1. `prevalidate` hook 1. `before` hook
2. Check backup location 2. Run backup
3. `before` hook 3. `after` hook
4. Run backup 4. - `success` hook if no errors were found
5. `after` hook
6. - `success` hook if no errors were found
- `failure` hook if at least one error was encountered - `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 ## Environment variables

10
docs/pnpm-lock.yaml generated
View File

@@ -1332,8 +1332,8 @@ packages:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
dev: false dev: false
/katex@0.16.10: /katex@0.16.8:
resolution: {integrity: sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==} resolution: {integrity: sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==}
hasBin: true hasBin: true
dependencies: dependencies:
commander: 8.3.0 commander: 8.3.0
@@ -1811,7 +1811,7 @@ packages:
dependencies: dependencies:
'@types/katex': 0.16.3 '@types/katex': 0.16.3
devlop: 1.1.0 devlop: 1.1.0
katex: 0.16.10 katex: 0.16.8
micromark-factory-space: 2.0.0 micromark-factory-space: 2.0.0
micromark-util-character: 2.0.1 micromark-util-character: 2.0.1
micromark-util-symbol: 2.0.0 micromark-util-symbol: 2.0.0
@@ -2354,7 +2354,7 @@ packages:
github-slugger: 2.0.0 github-slugger: 2.0.0
graceful-fs: 4.2.11 graceful-fs: 4.2.11
gray-matter: 4.0.3 gray-matter: 4.0.3
katex: 0.16.10 katex: 0.16.8
lodash.get: 4.4.2 lodash.get: 4.4.2
next: 13.5.3(react-dom@18.2.0)(react@18.2.0) next: 13.5.3(react-dom@18.2.0)(react@18.2.0)
next-mdx-remote: 4.4.1(react-dom@18.2.0)(react@18.2.0) next-mdx-remote: 4.4.1(react-dom@18.2.0)(react@18.2.0)
@@ -2510,7 +2510,7 @@ packages:
'@types/katex': 0.16.3 '@types/katex': 0.16.3
hast-util-from-html-isomorphic: 2.0.0 hast-util-from-html-isomorphic: 2.0.0
hast-util-to-text: 4.0.0 hast-util-to-text: 4.0.0
katex: 0.16.10 katex: 0.16.8
unist-util-visit-parents: 6.0.1 unist-util-visit-parents: 6.0.1
vfile: 6.0.1 vfile: 6.0.1
dev: false dev: false

2
go.mod
View File

@@ -32,5 +32,5 @@ require (
golang.org/x/text v0.3.8 // indirect golang.org/x/text v0.3.8 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )

4
go.sum
View File

@@ -487,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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -143,7 +143,6 @@ func (b Backend) Exec(args []string) error {
return err return err
} }
options := ExecuteOptions{Envs: env} options := ExecuteOptions{Envs: env}
args = append(args, combineBackendOptions("exec", b)...)
_, out, err := ExecuteResticCommand(options, args...) _, out, err := ExecuteResticCommand(options, args...)
if err != nil { if err != nil {
colors.Error.Println(out) colors.Error.Println(out)
@@ -183,7 +182,6 @@ func (b Backend) ExecDocker(l Location, args []string) (int, string, error) {
case "s3": case "s3":
case "azure": case "azure":
case "gs": case "gs":
case "rest":
// No additional setup needed // No additional setup needed
case "rclone": case "rclone":
// Read host rclone config and mount it into the container // Read host rclone config and mount it into the container

View File

@@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path" "path"
@@ -36,7 +37,7 @@ func dlJSON(url string) (GithubRelease, error) {
return parsed, err return parsed, err
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return parsed, err return parsed, err
@@ -72,10 +73,9 @@ func downloadAndInstallAsset(body GithubRelease, name string) error {
// Uncompress // Uncompress
bz := bzip2.NewReader(resp.Body) bz := bzip2.NewReader(resp.Body)
// Save to tmp file in the same directory as the install directory // Save to tmp
// Linux does not support overwriting the file that is currently being running // Linux does not support overwriting the file that is currently being overwritten, but it can be deleted and a new one moved in its place.
// But it can be delete the old one and a new one moved in its place. tmp, err := ioutil.TempFile(os.TempDir(), "autorestic-")
tmp, err := os.CreateTemp(INSTALL_PATH, "autorestic-")
if err != nil { if err != nil {
return err return err
} }
@@ -89,24 +89,22 @@ func downloadAndInstallAsset(body GithubRelease, name string) error {
to := path.Join(INSTALL_PATH, name) to := path.Join(INSTALL_PATH, name)
defer os.Remove(tmp.Name()) // Cleanup temporary file after thread exits defer os.Remove(tmp.Name()) // Cleanup temporary file after thread exits
if err := os.Rename(tmp.Name(), to); err != nil {
mode := os.FileMode(0755) colors.Error.Printf("os.Rename() failed (%v), retrying with io.Copy()\n", err.Error())
if originalBin, err := os.Lstat(to); err == nil { var src *os.File
mode = originalBin.Mode() var dst *os.File
err := os.Remove(to) if src, err = os.Open(tmp.Name()); err != nil {
if err != nil { return err
}
if dst, err = os.Create(to); err != nil {
return err
}
if _, err := io.Copy(dst, src); err != nil {
return err
}
if err := os.Chmod(to, 0755); err != nil {
return err return err
} }
}
err = os.Rename(tmp.Name(), to)
if err != nil {
return err
}
err = os.Chmod(to, mode)
if err != nil {
return err
} }
colors.Success.Printf("Successfully installed '%s' under %s\n", name, INSTALL_PATH) colors.Success.Printf("Successfully installed '%s' under %s\n", name, INSTALL_PATH)

View File

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

View File

@@ -33,12 +33,11 @@ const (
) )
type Hooks struct { type Hooks struct {
Dir string `mapstructure:"dir"` Dir string `mapstructure:"dir"`
PreValidate HookArray `mapstructure:"prevalidate,omitempty"` Before HookArray `mapstructure:"before,omitempty"`
Before HookArray `mapstructure:"before,omitempty"` After HookArray `mapstructure:"after,omitempty"`
After HookArray `mapstructure:"after,omitempty"` Success HookArray `mapstructure:"success,omitempty"`
Success HookArray `mapstructure:"success,omitempty"` Failure HookArray `mapstructure:"failure,omitempty"`
Failure HookArray `mapstructure:"failure,omitempty"`
} }
type LocationCopy = map[string][]string 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 { if err := l.validate(); err != nil {
errors = append(errors, err) errors = append(errors, err)
goto after goto after
} }
// Hooks after location validation // Hooks
if err := l.ExecuteHooks(l.Hooks.Before, options); err != nil { if err := l.ExecuteHooks(l.Hooks.Before, options); err != nil {
errors = append(errors, err) errors = append(errors, err)
goto after 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 { if err := l.ExecuteHooks(l.Hooks.After, options); err != nil {
errors = append(errors, err) errors = append(errors, err)
} }
after: after:
// Success/failure hooks
var commands []string var commands []string
var isSuccess = len(errors) == 0 var isSuccess = len(errors) == 0
if isSuccess { if isSuccess {