mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2024-12-22 16:26:25 +00:00
commit
de663f287c
17
CHANGELOG.md
17
CHANGELOG.md
@ -5,6 +5,23 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [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
|
## [1.3.0] - 2021-10-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/cupcakearmy/autorestic/internal"
|
"github.com/cupcakearmy/autorestic/internal"
|
||||||
"github.com/cupcakearmy/autorestic/internal/colors"
|
"github.com/cupcakearmy/autorestic/internal/colors"
|
||||||
@ -22,8 +23,13 @@ var backupCmd = &cobra.Command{
|
|||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
errors := 0
|
errors := 0
|
||||||
for _, name := range selected {
|
for _, name := range selected {
|
||||||
location, _ := internal.GetLocation(name)
|
var splitted = strings.Split(name, "@")
|
||||||
errs := location.Backup(false)
|
var specificBackend = ""
|
||||||
|
if len(splitted) > 1 {
|
||||||
|
specificBackend = splitted[1]
|
||||||
|
}
|
||||||
|
location, _ := internal.GetLocation(splitted[0])
|
||||||
|
errs := location.Backup(false, specificBackend)
|
||||||
for err := range errs {
|
for err := range errs {
|
||||||
colors.Error.Println(err)
|
colors.Error.Println(err)
|
||||||
errors++
|
errors++
|
||||||
|
@ -9,12 +9,12 @@ var uninstallCmd = &cobra.Command{
|
|||||||
Use: "uninstall",
|
Use: "uninstall",
|
||||||
Short: "Uninstall restic and autorestic",
|
Short: "Uninstall restic and autorestic",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
noRestic, _ := cmd.Flags().GetBool("no-restic")
|
restic, _ := cmd.Flags().GetBool("restic")
|
||||||
bins.Uninstall(!noRestic)
|
bins.Uninstall(restic)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(uninstallCmd)
|
rootCmd.AddCommand(uninstallCmd)
|
||||||
uninstallCmd.Flags().Bool("no-restic", false, "do not uninstall restic.")
|
uninstallCmd.Flags().Bool("restic", false, "also uninstall restic.")
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ var upgradeCmd = &cobra.Command{
|
|||||||
Use: "upgrade",
|
Use: "upgrade",
|
||||||
Short: "Upgrade autorestic and restic",
|
Short: "Upgrade autorestic and restic",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
noRestic, _ := cmd.Flags().GetBool("no-restic")
|
restic, _ := cmd.Flags().GetBool("restic")
|
||||||
err := bins.Upgrade(!noRestic)
|
err := bins.Upgrade(restic)
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(upgradeCmd)
|
rootCmd.AddCommand(upgradeCmd)
|
||||||
upgradeCmd.Flags().Bool("no-restic", false, "also update restic")
|
upgradeCmd.Flags().Bool("restic", true, "also update restic")
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ In theory [all the restic backends](https://restic.readthedocs.io/en/stable/030_
|
|||||||
|
|
||||||
Those tested are the following:
|
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
|
## Local
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -1,36 +1,67 @@
|
|||||||
# Environment
|
# 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.
|
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
|
```yaml | autorestic.yaml
|
||||||
backend:
|
backend:
|
||||||
foo:
|
foo:
|
||||||
type: ...
|
type: ...
|
||||||
path: ...
|
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:
|
This means we could remove `key: secret123` from `.autorestic.yaml` and execute as follows:
|
||||||
|
|
||||||
```bash
|
```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`.
|
```yaml | autorestic.yaml
|
||||||
|
backends:
|
||||||
```| .autorestic.env
|
bb:
|
||||||
AUTORESTIC_FOO_KEY=secret123
|
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
|
> :ToCPrevNext
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
# Options
|
# Options
|
||||||
|
|
||||||
For the `backup` and `forget` commands you can pass any native flags to `restic`.
|
> ℹ️ For more detail see the [location docs](/location/options) for options, as they are the same.
|
||||||
|
|
||||||
> It is also possible to set options for an [a specific location](/location/options).
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
backend:
|
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.
|
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
|
> :ToCPrevNext
|
||||||
|
@ -14,4 +14,12 @@ autorestic backup -a
|
|||||||
autorestic backup -l foo -l bar
|
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
|
> :ToCPrevNext
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
Linux & macOS. Windows is not supported. If you have problems installing please open an issue :)
|
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
|
```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
|
## Alternatives
|
||||||
|
@ -1,8 +1,32 @@
|
|||||||
# Options
|
# 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 abc --tag` to the native command.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
locations:
|
locations:
|
||||||
@ -16,8 +40,22 @@ locations:
|
|||||||
- bar
|
- bar
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, whenever `autorestic` runs `restic backup` it will append a `--tag abc --tag` to the native command.
|
## Global Options
|
||||||
|
|
||||||
> For flags without arguments you can set them to `true`. They will be handled accordingly.
|
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
|
> :ToCPrevNext
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```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.
|
See [installation](/installation) for alternative options.
|
||||||
|
@ -31,7 +31,7 @@ else
|
|||||||
fi
|
fi
|
||||||
echo $ARCH
|
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}" \
|
| grep "browser_download_url.*_${OS}_${ARCH}" \
|
||||||
| cut -d : -f 2,3 \
|
| cut -d : -f 2,3 \
|
||||||
| tr -d \" \
|
| tr -d \" \
|
||||||
|
@ -58,20 +58,27 @@ func (b Backend) generateRepo() (string, error) {
|
|||||||
|
|
||||||
func (b Backend) getEnv() (map[string]string, error) {
|
func (b Backend) getEnv() (map[string]string, error) {
|
||||||
env := make(map[string]string)
|
env := make(map[string]string)
|
||||||
|
// Key
|
||||||
if b.Key != "" {
|
if b.Key != "" {
|
||||||
env["RESTIC_PASSWORD"] = 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()
|
repo, err := b.generateRepo()
|
||||||
env["RESTIC_REPOSITORY"] = repo
|
env["RESTIC_REPOSITORY"] = repo
|
||||||
for key, value := range b.Env {
|
for key, value := range b.Env {
|
||||||
env[strings.ToUpper(key)] = value
|
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
|
return env, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,17 +92,6 @@ func generateRandomKey() string {
|
|||||||
return key
|
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 {
|
func (b Backend) validate() error {
|
||||||
if b.Type == "" {
|
if b.Type == "" {
|
||||||
return fmt.Errorf(`Backend "%s" has no "type"`, b.name)
|
return fmt.Errorf(`Backend "%s" has no "type"`, b.name)
|
||||||
@ -105,8 +101,9 @@ func (b Backend) validate() error {
|
|||||||
}
|
}
|
||||||
if b.Key == "" {
|
if b.Key == "" {
|
||||||
// Check if key is set in environment
|
// Check if key is set in environment
|
||||||
if _, err := b.getKey(); err != nil {
|
env, _ := b.getEnv()
|
||||||
// If not generate a new one
|
if _, found := env["RESTIC_PASSWORD"]; !found {
|
||||||
|
// No key set in config file or env => generate random key and save file
|
||||||
key := generateRandomKey()
|
key := generateRandomKey()
|
||||||
b.Key = key
|
b.Key = key
|
||||||
c := GetConfig()
|
c := GetConfig()
|
||||||
|
@ -47,11 +47,11 @@ func dlJSON(url string) (GithubRelease, error) {
|
|||||||
|
|
||||||
func Uninstall(restic bool) error {
|
func Uninstall(restic bool) error {
|
||||||
if err := os.Remove(path.Join(INSTALL_PATH, "autorestic")); err != nil {
|
if err := os.Remove(path.Join(INSTALL_PATH, "autorestic")); err != nil {
|
||||||
colors.Error.Println(err)
|
return err
|
||||||
}
|
}
|
||||||
if restic {
|
if restic {
|
||||||
if err := os.Remove(path.Join(INSTALL_PATH, "restic")); err != nil {
|
if err := os.Remove(path.Join(INSTALL_PATH, "restic")); err != nil {
|
||||||
colors.Error.Println(err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -79,11 +79,15 @@ func downloadAndInstallAsset(body GithubRelease, name string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer tmp.Close()
|
defer tmp.Close()
|
||||||
tmp.Chmod(0755)
|
if err := tmp.Chmod(0755); err != nil {
|
||||||
io.Copy(tmp, bz)
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(tmp, bz); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
to := path.Join(INSTALL_PATH, name)
|
to := path.Join(INSTALL_PATH, name)
|
||||||
os.Remove(to) // Delete if current, ignore error if file does not exits.
|
defer os.Remove(to) // Delete if current, ignore error if file does not exits.
|
||||||
if err := os.Rename(tmp.Name(), to); err != nil {
|
if err := os.Rename(tmp.Name(), to); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -121,9 +125,11 @@ func Upgrade(restic bool) error {
|
|||||||
// Upgrade restic
|
// Upgrade restic
|
||||||
if restic {
|
if restic {
|
||||||
if err := InstallRestic(); err != nil {
|
if err := InstallRestic(); err != nil {
|
||||||
colors.Error.Println(err)
|
return err
|
||||||
|
}
|
||||||
|
if err := upgradeRestic(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
upgradeRestic()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade self
|
// Upgrade self
|
||||||
@ -140,7 +146,9 @@ func Upgrade(restic bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if current.LT(latest) {
|
if current.LT(latest) {
|
||||||
downloadAndInstallAsset(body, "autorestic")
|
if err := downloadAndInstallAsset(body, "autorestic"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
colors.Success.Println("Updated autorestic")
|
colors.Success.Println("Updated autorestic")
|
||||||
} else {
|
} else {
|
||||||
colors.Body.Println("Already up to date")
|
colors.Body.Println("Already up to date")
|
||||||
|
@ -16,16 +16,20 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "1.3.0"
|
const VERSION = "1.4.0"
|
||||||
|
|
||||||
var CI bool = false
|
var CI bool = false
|
||||||
var VERBOSE bool = false
|
var VERBOSE bool = false
|
||||||
var CRON_LEAN bool = false
|
var CRON_LEAN bool = false
|
||||||
|
|
||||||
|
type OptionMap map[string][]interface{}
|
||||||
|
type Options map[string]OptionMap
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Extras interface{} `yaml:"extras"`
|
Extras interface{} `yaml:"extras"`
|
||||||
Locations map[string]Location `yaml:"locations"`
|
Locations map[string]Location `yaml:"locations"`
|
||||||
Backends map[string]Backend `yaml:"backends"`
|
Backends map[string]Backend `yaml:"backends"`
|
||||||
|
Global Options `yaml:"global"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
@ -185,20 +189,18 @@ func GetAllOrSelected(cmd *cobra.Command, backends bool) ([]string, error) {
|
|||||||
selected, _ = cmd.Flags().GetStringSlice("location")
|
selected, _ = cmd.Flags().GetStringSlice("location")
|
||||||
}
|
}
|
||||||
for _, s := range selected {
|
for _, s := range selected {
|
||||||
found := false
|
var splitted = strings.Split(s, "@")
|
||||||
for _, l := range list {
|
for _, l := range list {
|
||||||
if l == s {
|
if l == splitted[0] {
|
||||||
found = true
|
goto found
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if backends {
|
||||||
if backends {
|
return nil, fmt.Errorf("invalid backend \"%s\"", s)
|
||||||
return nil, fmt.Errorf("invalid backend \"%s\"", s)
|
} else {
|
||||||
} else {
|
return nil, fmt.Errorf("invalid location \"%s\"", s)
|
||||||
return nil, fmt.Errorf("invalid location \"%s\"", s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
found:
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(selected) == 0 {
|
if len(selected) == 0 {
|
||||||
@ -235,23 +237,40 @@ func (c *Config) SaveConfig() error {
|
|||||||
return viper.WriteConfig()
|
return viper.WriteConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOptions(options Options, key string) []string {
|
func optionToString(option string) string {
|
||||||
var selected []string
|
if !strings.HasPrefix(option, "-") {
|
||||||
for k, values := range options[key] {
|
return "--" + option
|
||||||
|
}
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendOptionsToSlice(str *[]string, options OptionMap) {
|
||||||
|
for key, values := range options {
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
// Bool
|
// Bool
|
||||||
asBool, ok := value.(bool)
|
asBool, ok := value.(bool)
|
||||||
if ok && asBool {
|
if ok && asBool {
|
||||||
selected = append(selected, fmt.Sprintf("--%s", k))
|
*str = append(*str, optionToString(key))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// String
|
// String
|
||||||
asString, ok := value.(string)
|
asString, ok := value.(string)
|
||||||
if ok {
|
if ok {
|
||||||
selected = append(selected, fmt.Sprintf("--%s", k), asString)
|
*str = append(*str, optionToString(key), asString)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
return selected
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,6 @@ type Hooks struct {
|
|||||||
Failure HookArray `yaml:"failure,omitempty"`
|
Failure HookArray `yaml:"failure,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options map[string]map[string][]interface{}
|
|
||||||
|
|
||||||
type Location struct {
|
type Location struct {
|
||||||
name string `yaml:",omitempty"`
|
name string `yaml:",omitempty"`
|
||||||
From string `yaml:"from,omitempty"`
|
From string `yaml:"from,omitempty"`
|
||||||
@ -125,8 +123,9 @@ func (l Location) getPath() (string, error) {
|
|||||||
return "", fmt.Errorf("could not get path for location \"%s\"", l.name)
|
return "", fmt.Errorf("could not get path for location \"%s\"", l.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Backup(cron bool) []error {
|
func (l Location) Backup(cron bool, specificBackend string) []error {
|
||||||
var errors []error
|
var errors []error
|
||||||
|
var backends []string
|
||||||
colors.PrimaryPrint(" Backing up location \"%s\" ", l.name)
|
colors.PrimaryPrint(" Backing up location \"%s\" ", l.name)
|
||||||
t := l.getType()
|
t := l.getType()
|
||||||
options := ExecuteOptions{
|
options := ExecuteOptions{
|
||||||
@ -155,7 +154,17 @@ func (l Location) Backup(cron bool) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Backup
|
// 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)
|
backend, _ := GetBackend(to)
|
||||||
colors.Secondary.Printf("Backend: %s\n", backend.name)
|
colors.Secondary.Printf("Backend: %s\n", backend.name)
|
||||||
env, err := backend.getEnv()
|
env, err := backend.getEnv()
|
||||||
@ -338,7 +347,7 @@ func (l Location) RunCron() error {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
if now.After(next) {
|
if now.After(next) {
|
||||||
lock.SetCron(l.name, now.Unix())
|
lock.SetCron(l.name, now.Unix())
|
||||||
l.Backup(true)
|
l.Backup(true, "")
|
||||||
} else {
|
} else {
|
||||||
if !CRON_LEAN {
|
if !CRON_LEAN {
|
||||||
colors.Body.Printf("Skipping \"%s\", not due yet.\n", l.name)
|
colors.Body.Printf("Skipping \"%s\", not due yet.\n", l.name)
|
||||||
|
@ -53,6 +53,9 @@ func ExecuteCommand(options ExecuteOptions, args ...string) (string, error) {
|
|||||||
|
|
||||||
func ExecuteResticCommand(options ExecuteOptions, args ...string) (string, error) {
|
func ExecuteResticCommand(options ExecuteOptions, args ...string) (string, error) {
|
||||||
options.Command = RESTIC_BIN
|
options.Command = RESTIC_BIN
|
||||||
|
var c = GetConfig()
|
||||||
|
var optionsAsString = getOptions(c.Global, "")
|
||||||
|
args = append(optionsAsString, args...)
|
||||||
return ExecuteCommand(options, args...)
|
return ExecuteCommand(options, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user