Compare commits

..

66 Commits

Author SHA1 Message Date
bbdae05199 changelog 2021-12-06 17:27:59 +01:00
389490c4ea Merge pull request #136 from cupcakearmy/1.5.1
1.5.1
2021-12-06 17:24:19 +01:00
21b83b4c89 docker docs 2021-12-06 17:20:06 +01:00
982f9e0b5c better error handling 2021-12-06 12:13:18 +01:00
0c37af5588 #140 Docker version pinning 2021-12-06 11:59:58 +01:00
e3c378f2a1 release day 2021-11-24 09:57:10 +01:00
3b541665ae changelog 2021-11-24 09:45:23 +01:00
e0b2c99ccd contrib 2021-11-24 09:45:19 +01:00
2b14e6b1af remove print 2021-11-24 09:35:31 +01:00
1810af8d02 Merge pull request #137 from g-a-c/issues/130
refactor downloading of binaries
2021-11-23 13:01:09 +01:00
252968e15e ensure config is loaded before lock 2021-11-23 12:32:35 +01:00
26de4385ea version bump 2021-11-23 12:32:22 +01:00
0c71bea93e use own docker image 2021-11-21 21:10:32 +01:00
3029259d82 docs 2021-11-21 21:06:25 +01:00
Gavin Chappell
389f7c25dd refactor downloading of binaries
* If `/tmp` is a `tmpfs` or somehow not the same filesystem, `os.Rename()` will fail so use `io.Copy()` instead
* don't defer cleanup of `to` as this removes the newly-created file if the operation is successful, making `install` and `upgrade` _functionally_ `uninstall`
* defer cleanup of the temporary file since it will still be in place if `os.Rename()` fails

Fixes: #130
2021-11-20 19:30:46 +00:00
e055e28cfe Merge pull request #125 from cupcakearmy/1.5.0
1.5.0
2021-11-20 17:11:44 +01:00
ed3c17d678 migration docs 2021-11-20 17:09:42 +01:00
8802b74b47 changelog and docs 2021-11-20 17:03:54 +01:00
e94e7030fc docker image 2021-11-20 16:59:13 +01:00
c55e91b8ff version bump 2021-11-20 16:55:20 +01:00
170bdb81ad tags 2021-11-07 11:48:03 +01:00
4126436f7f migration docs 2021-11-07 11:42:45 +01:00
113a97c283 add config version to ensure compatibility 2021-11-06 22:00:44 +01:00
c250391f67 docs 2021-11-01 11:19:27 +01:00
d3b4915d25 deprecation notion 2021-11-01 09:09:29 +01:00
cd7a5cbc13 also enable azure and google cloud 2021-11-01 00:19:32 +01:00
3dd3956d64 support for rclone 2021-11-01 00:16:54 +01:00
59035da46a remove output 2021-10-31 23:58:08 +01:00
90914d2078 changelog 2021-10-31 23:35:52 +01:00
0ae374cd45 docs 2021-10-31 23:35:46 +01:00
3665cea62d foo 2021-10-31 23:11:57 +01:00
cf92d400c2 changelog 2021-10-31 23:11:06 +01:00
696bd14ac7 Merge pull request #124 from cupcakearmy/multiple-paths
Multiple paths
2021-10-31 23:09:23 +01:00
a68e3e426e simplify options handling 2021-10-31 23:07:12 +01:00
27e82c8529 Merge branch 'master' into multiple-paths 2021-10-31 22:54:14 +01:00
7f9251f06b Merge pull request #123 from cupcakearmy/1.4.1
allow all values from envs
2021-10-31 22:47:33 +01:00
09cfa4a98e changelog 2021-10-31 22:46:37 +01:00
05c3458a95 version bump 2021-10-31 22:45:54 +01:00
2826f9586d allow all values from envs 2021-10-31 22:45:03 +01:00
4fe241e6f3 support for multiple sources 2021-10-31 22:33:02 +01:00
d0b1b86fdd docker runner 2021-10-31 22:32:55 +01:00
14dd41d60d docs 2021-10-31 22:32:34 +01:00
bcfc734cd1 describe multiple sources 2021-10-31 22:32:30 +01:00
6817f494ef util to check if volume exists 2021-10-31 22:32:01 +01:00
2c46f0da0c restore arg help 2021-10-31 22:31:47 +01:00
ac756dfbde bug not showing error messages 2021-10-31 22:31:31 +01:00
f71425be5b Merge pull request #122 from n194/patch-1
Remove credit from AUR
2021-10-31 14:47:16 +01:00
n
d78fbb6650 Remove credit from AUR 2021-10-31 19:40:53 +09:00
7ad6f7ce9e Merge remote-tracking branch 'origin/master' into multiple-paths 2021-10-30 15:06:13 +02:00
de663f287c Merge pull request #118 from cupcakearmy/1.4.0
1.4.0
2021-10-30 15:02:27 +02:00
439076d7ab chengelog 2021-10-30 14:54:41 +02:00
8c30134f7c error handling for upgrade and uninstall 2021-10-30 14:50:27 +02:00
11d1da7468 use wget 2021-10-30 14:38:53 +02:00
92b1577634 use wget 2021-10-30 14:36:25 +02:00
a7944aed1f don't install restic as default 2021-10-30 14:36:16 +02:00
3b99e301e9 only use wget 2021-10-30 14:30:53 +02:00
246e6fc0d8 enable generic env variables 2021-10-30 13:48:44 +02:00
c2f9ed9204 multiple paths 2021-10-30 13:01:31 +02:00
4055ebf8e8 changelog 2021-10-29 18:35:24 +02:00
6be0a80b29 use built in function 2021-10-29 18:35:21 +02:00
efd4a7dfea version bump 2021-10-28 18:10:33 +02:00
440609220c support for global flags 2021-10-28 18:10:14 +02:00
fb6217d868 docs for global flags 2021-10-28 18:09:38 +02:00
1628384e1f docs 2021-10-28 17:35:51 +02:00
3e80e6d18e Merge pull request #117 from cupcakearmy/specific-location
specific location
2021-10-28 17:30:51 +02:00
83905d2993 specific location 2021-10-28 17:29:32 +02:00
36 changed files with 677 additions and 216 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
*
!**/*.go
!build
!cmd
!internal
!go.*

View File

@@ -3,16 +3,44 @@ name: Main
on:
push:
tags:
- 'v*.*.*'
- "v*.*.*"
jobs:
build:
docker:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker Labels
id: meta
uses: crazy-max/ghaction-docker-meta@v2
with:
images: cupcakearmy/autorestic
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.16.3'
go-version: "^1.16.3"
- name: Build
run: go run build/build.go
@@ -25,4 +53,4 @@ jobs:
with:
files: dist/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -5,13 +5,68 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.5.1] - 2021-12-06
### Changed
- use official docker image instead of installing rclone every time docker is used.
- docker docs
### Fixed
- lock file not always next to the config file.
- update / install bugs.
- lock docker image tag to the current autorestic version
- better error logging
## [1.5.0] - 2021-11-20
### Added
- Support for multiple paths.
- Improved error handling.
- Allow for specific snapshot to be restored.
- Docker image.
### Fixed
- rclone in docker volumes.
### Changed
- [Breaking Change] Declaration of docker volumes. See: https://autorestic.vercel.app/migration/1.4_1.5.
- [Breaking Change] Hooks default executing directory now defaults to the config file directory. See: https://autorestic.vercel.app/migration/1.4_1.5.
## [1.4.1] - 2021-10-31
### Fixed
- Numeric values from config files not being passed to env.
## [1.4.0] - 2021-10-30
### Added
- Allow specify to specify a backend for location backup.
- Global restic flags.
- Generic ENV support for backends.
### Changed
- Install now only requires `wget`.
- Env variable for the `KEY` has been renamed from `AUTORESTIC_[BACKEND NAME]_KEY` -> `AUTORESTIC_[BACKEND NAME]_RESTIC_PASSWORD`.
### Fixed
- Error handling during upgrade & uninstall.
## [1.3.0] - 2021-10-26
### Added
- Pass restic backup metadata as ENV to hooks
- Pass restic backup metadata as ENV to hooks.
- Support for `XDG_CONFIG_HOME` and `${HOME}/.config` as default locations for `.autorestic.yaml` file.
- Binary restic flags are now supported
- Binary restic flags are now supported.
- Pass encryption keys from env variables or files.
## [1.2.0] - 2021-08-05
@@ -19,12 +74,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Community page
- Support for yaml references and aliases
- Support for yaml references and aliases.
### Fixed
- Better verbose output for hooks
- Better error message for bad formatted configs
- Better verbose output for hooks.
- Better error message for bad formatted configs.
## [1.1.2] - 2021-07-11
@@ -36,24 +91,24 @@ Don't check all backend when running `forget` or `exec` commands.
### Added
- Options for backends
- Options for backends.
## [1.1.0] - 2021-05-06
### Added
- use custom restic binary
- success & failure hooks
- use custom restic binary.
- success & failure hooks.
### Fixed
- don't skip other locations on failure
- don't skip other locations on failure.
## [1.0.9] - 2021-05-01
### Fixed
- Validation for docker volumes
- Validation for docker volumes.
## [1.0.8] - 2021-04-28
@@ -76,7 +131,7 @@ Don't check all backend when running `forget` or `exec` commands.
### Added
- Support for rclone
- Support for rclone.
## [1.0.5] - 2021-04-24
@@ -89,17 +144,17 @@ Don't check all backend when running `forget` or `exec` commands.
### Added
- Options to add rest username and password in config
- Options to add rest username and password in config.
### Fixed
- Don't add empty strings when saving config
- Don't add empty strings when saving config.
## [1.0.3] - 2021-04-20
### Fixed
- Auto upgrade script was not working on linux as linux does not support writing to the binary that is being executed
- Auto upgrade script was not working on linux as linux does not support writing to the binary that is being executed.
## [1.0.2] - 2021-04-20
@@ -115,7 +170,7 @@ Don't check all backend when running `forget` or `exec` commands.
### Added
- Completion command for various shells
- Completion command for various shells.
## [1.0.0] - 2021-04-17

12
Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM golang:1.16-alpine as builder
WORKDIR /app
COPY go.* .
RUN go mod download
COPY . .
RUN go build
FROM alpine
RUN apk add --no-cache restic rclone
COPY --from=builder /app/autorestic /usr/bin/autorestic
CMD [ "autorestic" ]

View File

@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"strings"
"github.com/cupcakearmy/autorestic/internal"
"github.com/cupcakearmy/autorestic/internal/colors"
@@ -22,10 +23,15 @@ var backupCmd = &cobra.Command{
CheckErr(err)
errors := 0
for _, name := range selected {
location, _ := internal.GetLocation(name)
errs := location.Backup(false)
for err := range errs {
colors.Error.Println(err)
var splitted = strings.Split(name, "@")
var specificBackend = ""
if len(splitted) > 1 {
specificBackend = splitted[1]
}
location, _ := internal.GetLocation(splitted[0])
errs := location.Backup(false, specificBackend)
for _, err := range errs {
colors.Error.Printf("%s\n\n", err)
errors++
}
}

View File

@@ -11,6 +11,7 @@ var checkCmd = &cobra.Command{
Use: "check",
Short: "Check if everything is setup",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
CheckErr(err)
defer lock.Unlock()

View File

@@ -11,6 +11,7 @@ var cronCmd = &cobra.Command{
Short: "Run cron job for automated backups",
Long: `Intended to be mainly triggered by an automated system like systemd or crontab. For each location checks if a cron backup is due and runs it.`,
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
internal.CRON_LEAN, _ = cmd.Flags().GetBool("lean")
err := lock.Lock()
CheckErr(err)

View File

@@ -9,9 +9,11 @@ import (
)
var restoreCmd = &cobra.Command{
Use: "restore",
Use: "restore [snapshot id]",
Short: "Restore backup for location",
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
CheckErr(err)
defer lock.Unlock()
@@ -24,7 +26,11 @@ var restoreCmd = &cobra.Command{
target, _ := cmd.Flags().GetString("to")
from, _ := cmd.Flags().GetString("from")
force, _ := cmd.Flags().GetBool("force")
err = l.Restore(target, from, force)
snapshot := ""
if len(args) > 0 {
snapshot = args[0]
}
err = l.Restore(target, from, force, snapshot)
CheckErr(err)
},
}

View File

@@ -9,12 +9,12 @@ var uninstallCmd = &cobra.Command{
Use: "uninstall",
Short: "Uninstall restic and autorestic",
Run: func(cmd *cobra.Command, args []string) {
noRestic, _ := cmd.Flags().GetBool("no-restic")
bins.Uninstall(!noRestic)
restic, _ := cmd.Flags().GetBool("restic")
bins.Uninstall(restic)
},
}
func init() {
rootCmd.AddCommand(uninstallCmd)
uninstallCmd.Flags().Bool("no-restic", false, "do not uninstall restic.")
uninstallCmd.Flags().Bool("restic", false, "also uninstall restic.")
}

View File

@@ -9,13 +9,13 @@ var upgradeCmd = &cobra.Command{
Use: "upgrade",
Short: "Upgrade autorestic and restic",
Run: func(cmd *cobra.Command, args []string) {
noRestic, _ := cmd.Flags().GetBool("no-restic")
err := bins.Upgrade(!noRestic)
restic, _ := cmd.Flags().GetBool("restic")
err := bins.Upgrade(restic)
CheckErr(err)
},
}
func init() {
rootCmd.AddCommand(upgradeCmd)
upgradeCmd.Flags().Bool("no-restic", false, "also update restic")
upgradeCmd.Flags().Bool("restic", true, "also update restic")
}

View File

@@ -40,7 +40,13 @@
> [Uninstall](/cli/uninstall)
> [Upgrade](/cli/upgrade)
> :Collapse label=Migration
>
> [0.x → 1.0](/migration/0.x_1.0)
> [1.4 → 1.5](/migration/1.4_1.5)
[Examples](/examples)
[Docker](/docker)
[QA](/qa)
[Community](/community)
[Contributors](/contrib)

View File

@@ -4,6 +4,8 @@ In theory [all the restic backends](https://restic.readthedocs.io/en/stable/030_
Those tested are the following:
> You can also [specify the `env` variables in a config file](/backend/env) to separate them from the config file.
## Local
```yaml

View File

@@ -1,36 +1,67 @@
# Environment
> ⚠ Available since version `v1.3.0`
> ⚠ Available since version `v1.4.0`
Sometimes it's favorable not having the encryption keys in the config files.
For that `autorestic` allows passing the backend keys as `ENV` variables, or through an env file.
For that `autorestic` allows passing the env variables to backend password as `ENV` variables, or through an env file.
You can also pass whatever `env` variable to restic by prefixing it with `AUTORESTIC_[BACKEND NAME]_`.
The syntax for the `ENV` variables is as follows: `AUTORESTIC_[BACKEND NAME]_KEY`.
> Env variables and file overwrite the config file in the following order:
>
> Env Variables > Env File (`.autorestic.env`) > Config file (`.autorestic.yaml`)
## Env file
Alternatively `autorestic` can load an env file, located next to `.autorestic.yml` called `.autorestic.env`.
```
AUTORESTIC_FOO_RESTIC_PASSWORD=secret123
```
### Example with repository password
The syntax for the `ENV` variables is as follows: `AUTORESTIC_[BACKEND NAME]_RESTIC_PASSWORD`.
```yaml | autorestic.yaml
backend:
foo:
type: ...
path: ...
key: secret123 # => AUTORESTIC_FOO_KEY=secret123
key: secret123 # => AUTORESTIC_FOO_RESTIC_PASSWORD=secret123
```
## Example
This means we could remove `key: secret123` from `.autorestic.yaml` and execute as follows:
```bash
AUTORESTIC_FOO_KEY=secret123 autorestic backup ...
AUTORESTIC_FOO_RESTIC_PASSWORD=secret123 autorestic backup ...
```
## Env file
### Example with Backblaze B2
Alternatively `autorestic` can load an env file, located next to `autorestic.yml` called `.autorestic.env`.
```| .autorestic.env
AUTORESTIC_FOO_KEY=secret123
```yaml | autorestic.yaml
backends:
bb:
type: b2
path: myBucket
key: myPassword
env:
B2_ACCOUNT_ID: 123
B2_ACCOUNT_KEY: 456
```
after that you can simply use `autorestic` as your are used to.
You could create an `.autorestic.env` or pass the following `ENV` variables to autorestic:
```
AUTORESTIC_BB_RESTIC_PASSWORD=myPassword
AUTORESTIC_BB_B2_ACCOUNT_ID=123
AUTORESTIC_BB_B2_ACCOUNT_KEY=456
```
```yaml | autorestic.yaml
backends:
bb:
type: b2
path: myBucket
```
> :ToCPrevNext

View File

@@ -1,8 +1,6 @@
# Options
For the `backup` and `forget` commands you can pass any native flags to `restic`.
> It is also possible to set options for an [a specific location](/location/options).
> For more detail see the [location docs](/location/options) for options, as they are the same.
```yaml
backend:
@@ -18,8 +16,4 @@ backend:
In this example, whenever `autorestic` runs `restic backup` it will append a `--tag abc --tag` to the native command.
For more detail see the [location docs](/location/options) for options, as they are the same.
> For flags without arguments you can set them to `true`. They will be handled accordingly.
> :ToCPrevNext

View File

@@ -3,6 +3,8 @@
Backends are the outputs of the backup process. Each location needs at least one.
```yaml | .autorestic.yml
version: 2
backends:
name-of-backend:
type: local

View File

@@ -14,4 +14,12 @@ autorestic backup -a
autorestic backup -l foo -l bar
```
## Specific location
`autorestic` also allows selecting specific backends for a location with the `location@backend` syntax.
```bash
autorestic backup -l location@backend
```
> :ToCPrevNext

View File

@@ -1,12 +1,12 @@
# Restore
```bash
autorestic restore [-l, --location] [--from backend] [--to <out dir>] [-f, --force]
autorestic restore [-l, --location] [--from backend] [--to <out dir>] [-f, --force] [snapshot]
```
This will restore all the locations to the selected target. If for one location there are more than one backends specified autorestic will take the first one.
This will restore the location to the selected target. If for one location there are more than one backends specified autorestic will take the first one. If no specific snapshot is specified `autorestic` will use `latest`.
The `--to` path has to be empty as no data will be overwritten by default. If you are sure you can pass the `-f, --force` flag and the data will be overwritten in the destination. However note that this will overwrite all the data existent in the backup, not only the 1 file that is missing e.g.
If you are sure you can pass the `-f, --force` flag and the data will be overwritten in the destination. However note that this will overwrite all the data existent in the backup, not only the 1 file that is missing e.g.
## Example

View File

@@ -16,6 +16,8 @@ You can also specify a custom file with the `-c path/to/some/config.yml`
## Example configuration
```yaml | .autorestic.yml
version: 2
locations:
home:
from: /home/me
@@ -49,6 +51,8 @@ Aliases allow to reuse snippets of config throughout the same file.
The following example shows how the locations `a` and `b` share the same hooks and forget policies.
```yaml | .autorestic.yml
version: 2
extras:
hooks: &foo
before:

View File

@@ -3,6 +3,7 @@
This amazing people helped the project!
- @agateblue - Docs, Pruning, S3
- @g-a-c - Update/Install bugs.
- @david-boles - Docs
- @SebDanielsson - Brew
- @n194 - AUR Package

28
docs/markdown/docker.md Normal file
View File

@@ -0,0 +1,28 @@
# 🐳 Docker
The docker image is build with rclone and restic already included. It's ment more as a utility image.
## Remote hosts
For remote backups (S3, B2, GCS, etc.) it's quite easy, as you only need to mount the config file and the data to backup.
```bash
docker run --rm \\
-v $(pwd):/data \\
cupcakearmy/autorestic \\
autorestic backup -va
```
## Rclone
For rclone you will have to also mount the rclone config file to `/root/.config/rclone/rclone.conf`.
To check where it is located you can run the following command: `rclone config file`.
**Example**
```bash
docker run \\
-v /home/user/.config/rclone/rclone.conf:/root/.config/rclone/rclone.conf:ro \\
...
```

View File

@@ -2,14 +2,20 @@
Linux & macOS. Windows is not supported. If you have problems installing please open an issue :)
Autorestic requires `curl`, `wget` and `bzip2` to be installed. For most systems these should be already installed.
Autorestic requires `bash`, `wget` and `bzip2` to be installed. For most systems these should be already installed.
```bash
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
wget -qO - https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
```
## Alternatives
### Docker
There is an official docker image over at [cupcakearmy/autorestic](https://hub.docker.com/r/cupcakearmy/autorestic).
For some examples see [here](/docker).
### Manual
You can download the right binary from the release page and simply copy it to `/usr/local/bin` or whatever path you prefer. Autoupdates will still work.
@@ -20,6 +26,6 @@ If you are on macOS you can install through brew: `brew install autorestic`.
### AUR
If you are on Arch there is an [AUR Package](https://aur.archlinux.org/packages/autorestic-bin/) by @n194.
~~If you are on Arch there is an [AUR Package](https://aur.archlinux.org/packages/autorestic-bin/) (looking for maintainers).~~ - Deprecated
> :ToCPrevNext

View File

@@ -3,7 +3,7 @@
autorestic supports docker volumes directly, without needing them to be mounted to the host filesystem.
```yaml | docker-compose.yml
version: '3.7'
version: '3.8'
volumes:
data:
@@ -18,13 +18,9 @@ services:
```yaml | .autorestic.yml
locations:
- name: hello
from: volume:my-data
to:
- remote
backends:
- name: remote
foo:
from: my-data
type: volume
# ...
```

View File

@@ -7,6 +7,8 @@ This is based on [Restic's snapshots policies](https://restic.readthedocs.io/en/
> **Note** This is a full example, of course you also can specify only one of them
```yaml | .autorestic.yml
version: 2
locations:
etc:
from: /etc
@@ -22,4 +24,17 @@ locations:
keep-within: '2w' # keep snapshots from the last 2 weeks
```
## Globally
You can specify global forget policies that would be applied to all locations:
```yaml | .autorestic.yml
version: 2
global:
forget:
keep-daily: 30
keep-weekly: 52
```
> :ToCPrevNext

View File

@@ -1,8 +1,32 @@
# Options
For the `backup` and `forget` commands you can pass any native flags to `restic`.
For the `backup` and `forget` commands you can pass any native flags to `restic`. In addition you can specify flags for every command with `all`.
> It is also possible to set options for an [entire backend](/backend/options).
If flags don't start with `-` they will get prefixed with `--`.
Flags without arguments can be set to `true`. They will be handled accordingly.
> It is also possible to set options for an [entire backend](/backend/options) or globally (see below).
```yaml
locations:
foo:
# ...
options:
all:
some-flag: 123
# Equivalent to
--some-flag: 123
backup:
boolean-flag: true
tag:
- foo
- bar
```
## Example
In this example, whenever `autorestic` runs `restic backup` it will append a `--tag foo --tag bar` to the native command.
```yaml
locations:
@@ -16,8 +40,28 @@ locations:
- bar
```
In this example, whenever `autorestic` runs `restic backup` it will append a `--tag abc --tag` to the native command.
## Priority
> For flags without arguments you can set them to `true`. They will be handled accordingly.
Options can be set globally, on the backends or on the locations.
The priority is as follows: `location > backend > global`.
## Global Options
It is possible to specify global flags that will be run every time restic is invoked. To do so specify them under `global` in your config file.
```yaml
global:
all:
cache-dir: ~/restic
backup:
tag:
- foo
backends:
# ...
locations:
# ...
```
> :ToCPrevNext

View File

@@ -4,9 +4,15 @@ Locations can be seen as the input to the backup process. Generally this is simp
The paths can be relative from the config file. A location can have multiple backends, so that the data is secured across multiple servers.
```yaml | .autorestic.yml
version: 2
locations:
my-location-name:
from: path/to/backup
# Or multiple
# from:
# - /a
# - /b
to:
- name-of-backend
- also-backup-to-this-backend
@@ -14,7 +20,7 @@ locations:
## `from`
This is the source of the location.
This is the source of the location. Can be an `array` for multiple sources.
#### How are paths resolved?

View File

@@ -1,6 +1,4 @@
# Upgrade
## From `0.x` to `1.0`
# From `0.x` to `1.0`
Most of the config file is remained compatible, however to clean up the backends custom environment variables were moved from the root object to an `env` object.

View File

@@ -0,0 +1,60 @@
# Migration from `1.4` to `1.5`
## ⚠️ Important notes
The way snapshots are referenced in the `restore` and `prune` commands has been changed. Before they were referenced by the path. Now every backup is tagged and those tags are then referenced in the cli. This means that when running restore and forget commands old backups are not taken into account anymore.
## Config files
- The config file now required to have a version number. This has to be added with `version: 2` at the root.
- Hooks now optionally support `dir: /some/dir` in the [options object](https://pkg.go.dev/github.com/cupcakearmy/autorestic/internal#Hooks).
- Docker volumes don't get prefixed with `volume:` anymore, rather you have to set the `type: volume` in the [location config](https://pkg.go.dev/github.com/cupcakearmy/autorestic/internal#Hooks).
See detailed instructions below.
## Config Version
```yaml
version: 2 # Added
backends:
# ...
```
## Hooks
Since `1.5` multiple sources for a location are possible.
For this reason, while before hooks where executed in the folder of the source, now they are executed in the directory of the config `.autorestic.yaml`.
You can overwrite this behavior with the new `dir` option in the hook section of the config.
```yaml
locations:
l1:
# ...
from: /foo/bar
hooks:
dir: /foo/bar
before: pwd
```
## Docker volumes
The syntax with docker volumes has changed and needs to be adjusted.
```yaml
# Before
locations:
foo:
from: volume:my-data
```
```yaml
# After
locations:
foo:
from: my-data
type: volume
```
> :ToCPrevNext

View File

@@ -0,0 +1,4 @@
# Migration
- [From 0.x to 1.0](/migration/0.x_1.0)
- [From 1.4 to 1.5](/migration/1.4_1.5)

View File

@@ -3,7 +3,7 @@
## Installation
```bash
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
wget -qO - https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
```
See [installation](/installation) for alternative options.
@@ -25,9 +25,15 @@ For a quick overview:
> Note that the data is automatically encrypted on the server. The key will be generated and added to your config file. Every backend will have a separate key. **You should keep a copy of the keys or config file somewhere in case your server dies**. Otherwise DATA IS LOST!
```yaml | .autorestic.yml
version: 2
locations:
home:
from: /home/me
from: /home
# Or multiple
# from:
# - /foo
# - /bar
to: remote
important:

View File

@@ -31,7 +31,7 @@ else
fi
echo $ARCH
curl -s https://api.github.com/repos/cupcakearmy/autorestic/releases/latest \
wget -qO - https://api.github.com/repos/cupcakearmy/autorestic/releases/latest \
| grep "browser_download_url.*_${OS}_${ARCH}" \
| cut -d : -f 2,3 \
| tr -d \" \

View File

@@ -58,20 +58,27 @@ func (b Backend) generateRepo() (string, error) {
func (b Backend) getEnv() (map[string]string, error) {
env := make(map[string]string)
// Key
if b.Key != "" {
env["RESTIC_PASSWORD"] = b.Key
} else {
key, err := b.getKey()
if err != nil {
return nil, err
}
env["RESTIC_PASSWORD"] = key
}
// From config file
repo, err := b.generateRepo()
env["RESTIC_REPOSITORY"] = repo
for key, value := range b.Env {
env[strings.ToUpper(key)] = value
}
// From Envfile and passed as env
var prefix = "AUTORESTIC_" + strings.ToUpper(b.name) + "_"
for _, variable := range os.Environ() {
var splitted = strings.SplitN(variable, "=", 2)
if strings.HasPrefix(splitted[0], prefix) {
env[strings.TrimPrefix(splitted[0], prefix)] = splitted[1]
}
}
return env, err
}
@@ -85,17 +92,6 @@ func generateRandomKey() string {
return key
}
func (b Backend) getKey() (string, error) {
if b.Key != "" {
return b.Key, nil
}
keyName := "AUTORESTIC_" + strings.ToUpper(b.name) + "_KEY"
if key, found := os.LookupEnv(keyName); found {
return key, nil
}
return "", fmt.Errorf("no key found for backend \"%s\"", b.name)
}
func (b Backend) validate() error {
if b.Type == "" {
return fmt.Errorf(`Backend "%s" has no "type"`, b.name)
@@ -105,8 +101,9 @@ func (b Backend) validate() error {
}
if b.Key == "" {
// Check if key is set in environment
if _, err := b.getKey(); err != nil {
// If not generate a new one
env, _ := b.getEnv()
if _, found := env["RESTIC_PASSWORD"]; !found {
// No key set in config file or env => generate random key and save file
key := generateRandomKey()
b.Key = key
c := GetConfig()
@@ -160,30 +157,50 @@ func (b Backend) ExecDocker(l Location, args []string) (string, error) {
if err != nil {
return "", err
}
volume := l.getVolumeName()
path, _ := l.getPath()
volume := l.From[0]
options := ExecuteOptions{
Command: "docker",
Envs: env,
}
dir := "/data"
args = append([]string{"restic"}, args...)
docker := []string{
"run", "--rm",
"--pull", "always",
"--entrypoint", "ash",
"--workdir", path,
"--volume", volume + ":" + path,
"--workdir", dir,
"--volume", volume + ":" + dir,
}
// Use of docker host, not the container host
if hostname, err := os.Hostname(); err == nil {
docker = append(docker, "--hostname", hostname)
}
if b.Type == "local" {
switch b.Type {
case "local":
actual := env["RESTIC_REPOSITORY"]
docker = append(docker, "--volume", actual+":"+"/repo")
env["RESTIC_REPOSITORY"] = "/repo"
case "b2":
case "s3":
case "azure":
case "gs":
// No additional setup needed
case "rclone":
// Read host rclone config and mount it into the container
configFile, err := ExecuteCommand(ExecuteOptions{Command: "rclone"}, "config", "file")
if err != nil {
return "", err
}
splitted := strings.Split(strings.TrimSpace(configFile), "\n")
configFilePath := splitted[len(splitted)-1]
docker = append(docker, "--volume", configFilePath+":"+"/root/.config/rclone/rclone.conf:ro")
default:
return "", fmt.Errorf("Backend type \"%s\" is not supported as volume endpoint", b.Type)
}
for key, value := range env {
docker = append(docker, "--env", key+"="+value)
}
docker = append(docker, "restic/restic", "-c", "restic "+strings.Join(args, " "))
docker = append(docker, "cupcakearmy/autorestic:"+VERSION, "-c", strings.Join(args, " "))
out, err := ExecuteCommand(options, docker...)
return out, err
}

View File

@@ -47,11 +47,11 @@ func dlJSON(url string) (GithubRelease, error) {
func Uninstall(restic bool) error {
if err := os.Remove(path.Join(INSTALL_PATH, "autorestic")); err != nil {
colors.Error.Println(err)
return err
}
if restic {
if err := os.Remove(path.Join(INSTALL_PATH, "restic")); err != nil {
colors.Error.Println(err)
return err
}
}
return nil
@@ -79,13 +79,31 @@ func downloadAndInstallAsset(body GithubRelease, name string) error {
return err
}
defer tmp.Close()
tmp.Chmod(0755)
io.Copy(tmp, bz)
if err := tmp.Chmod(0755); err != nil {
return err
}
if _, err := io.Copy(tmp, bz); err != nil {
return err
}
to := path.Join(INSTALL_PATH, name)
os.Remove(to) // Delete if current, ignore error if file does not exits.
defer os.Remove(tmp.Name()) // Cleanup temporary file after thread exits
if err := os.Rename(tmp.Name(), to); err != nil {
return nil
colors.Error.Printf("os.Rename() failed (%v), retrying with io.Copy()\n", err.Error())
var src *os.File
var dst *os.File
if src, err = os.Open(tmp.Name()); 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
}
}
colors.Success.Printf("Successfully installed '%s' under %s\n", name, INSTALL_PATH)
@@ -121,9 +139,11 @@ func Upgrade(restic bool) error {
// Upgrade restic
if restic {
if err := InstallRestic(); err != nil {
colors.Error.Println(err)
return err
}
if err := upgradeRestic(); err != nil {
return err
}
upgradeRestic()
}
// Upgrade self
@@ -140,7 +160,9 @@ func Upgrade(restic bool) error {
return err
}
if current.LT(latest) {
downloadAndInstallAsset(body, "autorestic")
if err := downloadAndInstallAsset(body, "autorestic"); err != nil {
return err
}
colors.Success.Println("Updated autorestic")
} else {
colors.Body.Println("Already up to date")

View File

@@ -16,22 +16,39 @@ import (
"github.com/spf13/viper"
)
const VERSION = "1.3.0"
const VERSION = "1.5.1"
var CI bool = false
var VERBOSE bool = false
var CRON_LEAN bool = false
type OptionMap map[string][]interface{}
type Options map[string]OptionMap
type Config struct {
Version string `yaml:"version"`
Extras interface{} `yaml:"extras"`
Locations map[string]Location `yaml:"locations"`
Backends map[string]Backend `yaml:"backends"`
Global Options `yaml:"global"`
}
var once sync.Once
var config *Config
func exitConfig(err error, msg string) {
if err != nil {
colors.Error.Println(err)
}
if msg != "" {
colors.Error.Println(msg)
}
lock.Unlock()
os.Exit(1)
}
func GetConfig() *Config {
if config == nil {
once.Do(func() {
if err := viper.ReadInConfig(); err == nil {
@@ -49,11 +66,24 @@ func GetConfig() *Config {
return
}
var versionConfig interface{}
viper.UnmarshalKey("version", &versionConfig)
if versionConfig == nil {
exitConfig(nil, "no version specified in config file. please see docs on how to migrate")
}
version, ok := versionConfig.(int)
if !ok {
exitConfig(nil, "version specified in config file is not an int")
} else {
// Check for version
if version != 2 {
exitConfig(nil, "unsupported config version number. please check the docs for migration\nhttps://autorestic.vercel.app/migration/")
}
}
config = &Config{}
if err := viper.UnmarshalExact(config); err != nil {
colors.Error.Println("Could not parse config file!")
lock.Unlock()
os.Exit(1)
exitConfig(err, "Could not parse config file!")
}
})
}
@@ -77,7 +107,11 @@ func (c *Config) Describe() {
var tmp string
colors.PrimaryPrint(`Location: "%s"`, name)
colors.PrintDescription("From", l.From)
tmp = ""
for _, path := range l.From {
tmp += fmt.Sprintf("\t%s %s\n", colors.Success.Sprint("←"), path)
}
colors.PrintDescription("From", tmp)
tmp = ""
for _, to := range l.To {
@@ -185,20 +219,18 @@ func GetAllOrSelected(cmd *cobra.Command, backends bool) ([]string, error) {
selected, _ = cmd.Flags().GetStringSlice("location")
}
for _, s := range selected {
found := false
var splitted = strings.Split(s, "@")
for _, l := range list {
if l == s {
found = true
break
if l == splitted[0] {
goto found
}
}
if !found {
if backends {
return nil, fmt.Errorf("invalid backend \"%s\"", s)
} else {
return nil, fmt.Errorf("invalid location \"%s\"", s)
}
if backends {
return nil, fmt.Errorf("invalid backend \"%s\"", s)
} else {
return nil, fmt.Errorf("invalid location \"%s\"", s)
}
found:
}
if len(selected) == 0 {
@@ -235,23 +267,47 @@ func (c *Config) SaveConfig() error {
return viper.WriteConfig()
}
func getOptions(options Options, key string) []string {
var selected []string
for k, values := range options[key] {
func optionToString(option string) string {
if !strings.HasPrefix(option, "-") {
return "--" + option
}
return option
}
func appendOptionsToSlice(str *[]string, options OptionMap) {
for key, values := range options {
for _, value := range values {
// Bool
asBool, ok := value.(bool)
if ok && asBool {
selected = append(selected, fmt.Sprintf("--%s", k))
continue
}
// String
asString, ok := value.(string)
if ok {
selected = append(selected, fmt.Sprintf("--%s", k), asString)
*str = append(*str, optionToString(key))
continue
}
*str = append(*str, optionToString(key), fmt.Sprint(value))
}
}
}
func getOptions(options Options, key string) []string {
var selected []string
var keys = []string{"all"}
if key != "" {
keys = append(keys, key)
}
for _, key := range keys {
appendOptionsToSlice(&selected, options[key])
}
return selected
}
func combineOptions(key string, l Location, b Backend) []string {
// Priority: location > backend > global
var options []string
gFlags := getOptions(GetConfig().Global, key)
bFlags := getOptions(b.Options, key)
lFlags := getOptions(l.Options, key)
options = append(options, gFlags...)
options = append(options, bFlags...)
options = append(options, lFlags...)
return options
}

View File

@@ -17,25 +17,24 @@ import (
type LocationType string
const (
TypeLocal LocationType = "local"
TypeVolume LocationType = "volume"
VolumePrefix string = "volume:"
TypeLocal LocationType = "local"
TypeVolume LocationType = "volume"
)
type HookArray = []string
type Hooks struct {
Dir string `yaml:"dir"`
Before HookArray `yaml:"before,omitempty"`
After HookArray `yaml:"after,omitempty"`
Success HookArray `yaml:"success,omitempty"`
Failure HookArray `yaml:"failure,omitempty"`
}
type Options map[string]map[string][]interface{}
type Location struct {
name string `yaml:",omitempty"`
From string `yaml:"from,omitempty"`
From []string `yaml:"from,omitempty"`
Type string `yaml:"type,omitempty"`
To []string `yaml:"to,omitempty"`
Hooks Hooks `yaml:"hooks,omitempty"`
Cron string `yaml:"cron,omitempty"`
@@ -49,21 +48,32 @@ func GetLocation(name string) (Location, bool) {
}
func (l Location) validate() error {
if l.From == "" {
if len(l.From) == 0 {
return fmt.Errorf(`Location "%s" is missing "from" key`, l.name)
}
if l.getType() == TypeLocal {
if from, err := GetPathRelativeToConfig(l.From); err != nil {
return err
} else {
if stat, err := os.Stat(from); err != nil {
t, err := l.getType()
if err != nil {
return err
}
switch t {
case TypeLocal:
for _, path := range l.From {
if from, err := GetPathRelativeToConfig(path); err != nil {
return err
} else {
if !stat.IsDir() {
return fmt.Errorf("\"%s\" is not valid directory for location \"%s\"", from, l.name)
if stat, err := os.Stat(from); err != nil {
return err
} else {
if !stat.IsDir() {
return fmt.Errorf("\"%s\" is not valid directory for location \"%s\"", from, l.name)
}
}
}
}
case TypeVolume:
if len(l.From) > 1 {
return fmt.Errorf(`location "%s" has more than one docker volume`, l.name)
}
}
if len(l.To) == 0 {
@@ -79,10 +89,17 @@ func (l Location) validate() error {
return nil
}
func ExecuteHooks(commands []string, options ExecuteOptions) error {
func (l Location) ExecuteHooks(commands []string, options ExecuteOptions) error {
if len(commands) == 0 {
return nil
}
if l.Hooks.Dir != "" {
if dir, err := GetPathRelativeToConfig(l.Hooks.Dir); err != nil {
return err
} else {
options.Dir = dir
}
}
colors.Secondary.Println("\nRunning hooks")
for _, command := range commands {
colors.Body.Println("> " + command)
@@ -99,38 +116,38 @@ func ExecuteHooks(commands []string, options ExecuteOptions) error {
return nil
}
func (l Location) getType() LocationType {
if strings.HasPrefix(l.From, VolumePrefix) {
return TypeVolume
func (l Location) getType() (LocationType, error) {
t := strings.ToLower(l.Type)
if t == "" || t == "local" {
return TypeLocal, nil
} else if t == "volume" {
return TypeVolume, nil
}
return TypeLocal
return "", fmt.Errorf("invalid location type \"%s\"", l.Type)
}
func (l Location) getVolumeName() string {
return strings.TrimPrefix(l.From, VolumePrefix)
func buildTag(parts ...string) string {
parts = append([]string{"ar"}, parts...)
return strings.Join(parts, ":")
}
func (l Location) getPath() (string, error) {
t := l.getType()
switch t {
case TypeLocal:
if path, err := GetPathRelativeToConfig(l.From); err != nil {
return "", err
} else {
return path, nil
}
case TypeVolume:
return "/volume/" + l.name + "/" + l.getVolumeName(), nil
}
return "", fmt.Errorf("could not get path for location \"%s\"", l.name)
func (l Location) getLocationTags() string {
return buildTag("location", l.name)
}
func (l Location) Backup(cron bool) []error {
func (l Location) Backup(cron bool, specificBackend string) []error {
var errors []error
var backends []string
colors.PrimaryPrint(" Backing up location \"%s\" ", l.name)
t := l.getType()
t, err := l.getType()
if err != nil {
errors = append(errors, err)
return errors
}
cwd, _ := GetPathRelativeToConfig(".")
options := ExecuteOptions{
Command: "bash",
Dir: cwd,
Envs: map[string]string{
"AUTORESTIC_LOCATION": l.name,
},
@@ -138,24 +155,27 @@ func (l Location) Backup(cron bool) []error {
if err := l.validate(); err != nil {
errors = append(errors, err)
colors.Error.Print(err)
goto after
}
if t == TypeLocal {
dir, _ := GetPathRelativeToConfig(l.From)
colors.Faint.Printf("Executing under: \"%s\"\n", dir)
options.Dir = dir
}
// Hooks
if err := ExecuteHooks(l.Hooks.Before, options); err != nil {
if err := l.ExecuteHooks(l.Hooks.Before, options); err != nil {
errors = append(errors, err)
goto after
}
// Backup
for i, to := range l.To {
if specificBackend == "" {
backends = l.To
} else {
if l.hasBackend(specificBackend) {
backends = []string{specificBackend}
} else {
errors = append(errors, fmt.Errorf("backup location \"%s\" has no backend \"%s\"", l.name, specificBackend))
return errors
}
}
for i, to := range backends {
backend, _ := GetBackend(to)
colors.Secondary.Printf("Backend: %s\n", backend.name)
env, err := backend.getEnv()
@@ -164,31 +184,40 @@ func (l Location) Backup(cron bool) []error {
continue
}
lFlags := getOptions(l.Options, "backup")
bFlags := getOptions(backend.Options, "backup")
cmd := []string{"backup"}
cmd = append(cmd, lFlags...)
cmd = append(cmd, bFlags...)
cmd = append(cmd, combineOptions("backup", l, backend)...)
if cron {
cmd = append(cmd, "--tag", "cron")
cmd = append(cmd, "--tag", buildTag("cron"))
}
cmd = append(cmd, ".")
cmd = append(cmd, "--tag", l.getLocationTags())
backupOptions := ExecuteOptions{
Dir: options.Dir,
Envs: env,
}
var out string
switch t {
case TypeLocal:
for _, from := range l.From {
path, err := GetPathRelativeToConfig(from)
if err != nil {
errors = append(errors, err)
goto after
}
cmd = append(cmd, path)
}
out, err = ExecuteResticCommand(backupOptions, cmd...)
case TypeVolume:
ok := CheckIfVolumeExists(l.From[0])
if !ok {
errors = append(errors, fmt.Errorf("volume \"%s\" does not exist", l.From[0]))
continue
}
cmd = append(cmd, "/data")
out, err = backend.ExecDocker(l, cmd)
}
if err != nil {
colors.Error.Println(out)
errors = append(errors, err)
errors = append(errors, fmt.Errorf("%s@%s:\n%s%s", l.name, backend.name, out, err))
continue
}
@@ -204,7 +233,7 @@ func (l Location) Backup(cron bool) []error {
}
// After hooks
if err := ExecuteHooks(l.Hooks.After, options); err != nil {
if err := l.ExecuteHooks(l.Hooks.After, options); err != nil {
errors = append(errors, err)
}
@@ -215,22 +244,19 @@ after:
} else {
commands = l.Hooks.Success
}
if err := ExecuteHooks(commands, options); err != nil {
if err := l.ExecuteHooks(commands, options); err != nil {
errors = append(errors, err)
}
colors.Success.Println("Done")
if len(errors) == 0 {
colors.Success.Println("Done")
}
return errors
}
func (l Location) Forget(prune bool, dry bool) error {
colors.PrimaryPrint("Forgetting for location \"%s\"", l.name)
path, err := l.getPath()
if err != nil {
return err
}
for _, to := range l.To {
backend, _ := GetBackend(to)
colors.Secondary.Printf("For backend \"%s\"\n", backend.name)
@@ -241,17 +267,14 @@ func (l Location) Forget(prune bool, dry bool) error {
options := ExecuteOptions{
Envs: env,
}
lFlags := getOptions(l.Options, "forget")
bFlags := getOptions(backend.Options, "forget")
cmd := []string{"forget", "--path", path}
cmd := []string{"forget", "--tag", l.getLocationTags()}
if prune {
cmd = append(cmd, "--prune")
}
if dry {
cmd = append(cmd, "--dry-run")
}
cmd = append(cmd, lFlags...)
cmd = append(cmd, bFlags...)
cmd = append(cmd, combineOptions("forget", l, backend)...)
out, err := ExecuteResticCommand(options, cmd...)
if VERBOSE {
colors.Faint.Println(out)
@@ -273,7 +296,7 @@ func (l Location) hasBackend(backend string) bool {
return false
}
func (l Location) Restore(to, from string, force bool) error {
func (l Location) Restore(to, from string, force bool, snapshot string) error {
if from == "" {
from = l.To[0]
} else if !l.hasBackend(from) {
@@ -284,16 +307,20 @@ func (l Location) Restore(to, from string, force bool) error {
if err != nil {
return err
}
colors.PrimaryPrint("Restoring location \"%s\"", l.name)
backend, _ := GetBackend(from)
path, err := l.getPath()
if err != nil {
return nil
if snapshot == "" {
snapshot = "latest"
}
colors.Secondary.Println("Restoring lastest snapshot")
colors.Body.Printf("%s → %s.\n", from, path)
switch l.getType() {
colors.PrimaryPrint("Restoring location \"%s\"", l.name)
backend, _ := GetBackend(from)
colors.Secondary.Printf("Restoring %s@%s → %s\n", snapshot, backend.name, to)
t, err := l.getType()
if err != nil {
return err
}
switch t {
case TypeLocal:
// Check if target is empty
if !force {
@@ -313,9 +340,9 @@ func (l Location) Restore(to, from string, force bool) error {
}
}
}
err = backend.Exec([]string{"restore", "--target", to, "--path", path, "latest"})
err = backend.Exec([]string{"restore", "--target", to, "--tag", l.getLocationTags(), snapshot})
case TypeVolume:
_, err = backend.ExecDocker(l, []string{"restore", "--target", ".", "--path", path, "latest"})
_, err = backend.ExecDocker(l, []string{"restore", "--target", "/", "--tag", l.getLocationTags(), snapshot})
}
if err != nil {
return err
@@ -338,7 +365,7 @@ func (l Location) RunCron() error {
now := time.Now()
if now.After(next) {
lock.SetCron(l.name, now.Unix())
l.Backup(true)
l.Backup(true, "")
} else {
if !CRON_LEAN {
colors.Body.Printf("Skipping \"%s\", not due yet.\n", l.name)

View File

@@ -19,8 +19,13 @@ func getLock() *viper.Viper {
once.Do(func() {
lock = viper.New()
lock.SetDefault("running", false)
p := path.Dir(viper.ConfigFileUsed())
file = path.Join(p, ".autorestic.lock.yml")
p := viper.ConfigFileUsed()
if p == "" {
colors.Error.Println("cannot lock before reading config location")
os.Exit(1)
}
file = path.Join(path.Dir(p), ".autorestic.lock.yml")
colors.Faint.Println("Using lock:\t", file)
lock.SetConfigFile(file)
lock.SetConfigType("yml")
lock.ReadInConfig()

View File

@@ -53,6 +53,9 @@ func ExecuteCommand(options ExecuteOptions, args ...string) (string, error) {
func ExecuteResticCommand(options ExecuteOptions, args ...string) (string, error) {
options.Command = RESTIC_BIN
var c = GetConfig()
var optionsAsString = getOptions(c.Global, "")
args = append(optionsAsString, args...)
return ExecuteCommand(options, args...)
}
@@ -74,3 +77,8 @@ func CopyFile(from, to string) error {
}
return nil
}
func CheckIfVolumeExists(volume string) bool {
_, err := ExecuteCommand(ExecuteOptions{Command: "docker"}, "volume", "inspect", volume)
return err == nil
}