mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2024-12-22 16:26:25 +00:00
commit
696bd14ac7
@ -5,6 +5,12 @@ 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.1] - 2021-10-31
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Numeric values from config files not being passed to env.
|
||||||
|
|
||||||
## [1.4.0] - 2021-10-30
|
## [1.4.0] - 2021-10-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -30,7 +30,7 @@ var backupCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
location, _ := internal.GetLocation(splitted[0])
|
location, _ := internal.GetLocation(splitted[0])
|
||||||
errs := location.Backup(false, specificBackend)
|
errs := location.Backup(false, specificBackend)
|
||||||
for err := range errs {
|
for _, err := range errs {
|
||||||
colors.Error.Println(err)
|
colors.Error.Println(err)
|
||||||
errors++
|
errors++
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var restoreCmd = &cobra.Command{
|
var restoreCmd = &cobra.Command{
|
||||||
Use: "restore",
|
Use: "restore [snapshot id]",
|
||||||
Short: "Restore backup for location",
|
Short: "Restore backup for location",
|
||||||
|
Args: cobra.MaximumNArgs(1),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
err := lock.Lock()
|
err := lock.Lock()
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
@ -24,7 +25,11 @@ var restoreCmd = &cobra.Command{
|
|||||||
target, _ := cmd.Flags().GetString("to")
|
target, _ := cmd.Flags().GetString("to")
|
||||||
from, _ := cmd.Flags().GetString("from")
|
from, _ := cmd.Flags().GetString("from")
|
||||||
force, _ := cmd.Flags().GetBool("force")
|
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)
|
CheckErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
# Restore
|
# Restore
|
||||||
|
|
||||||
```bash
|
```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
|
## Example
|
||||||
|
|
||||||
|
@ -20,6 +20,6 @@ If you are on macOS you can install through brew: `brew install autorestic`.
|
|||||||
|
|
||||||
### AUR
|
### 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).
|
||||||
|
|
||||||
> :ToCPrevNext
|
> :ToCPrevNext
|
||||||
|
@ -157,25 +157,32 @@ func (b Backend) ExecDocker(l Location, args []string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
volume := l.getVolumeName()
|
volume := l.From[0]
|
||||||
path, _ := l.getPath()
|
|
||||||
options := ExecuteOptions{
|
options := ExecuteOptions{
|
||||||
Command: "docker",
|
Command: "docker",
|
||||||
Envs: env,
|
Envs: env,
|
||||||
}
|
}
|
||||||
|
dir := "/data"
|
||||||
docker := []string{
|
docker := []string{
|
||||||
"run", "--rm",
|
"run", "--rm",
|
||||||
"--entrypoint", "ash",
|
"--entrypoint", "ash",
|
||||||
"--workdir", path,
|
"--workdir", dir,
|
||||||
"--volume", volume + ":" + path,
|
"--volume", volume + ":" + dir,
|
||||||
}
|
}
|
||||||
|
// Use of docker host, not the container host
|
||||||
if hostname, err := os.Hostname(); err == nil {
|
if hostname, err := os.Hostname(); err == nil {
|
||||||
docker = append(docker, "--hostname", hostname)
|
docker = append(docker, "--hostname", hostname)
|
||||||
}
|
}
|
||||||
if b.Type == "local" {
|
switch b.Type {
|
||||||
|
case "local":
|
||||||
actual := env["RESTIC_REPOSITORY"]
|
actual := env["RESTIC_REPOSITORY"]
|
||||||
docker = append(docker, "--volume", actual+":"+"/repo")
|
docker = append(docker, "--volume", actual+":"+"/repo")
|
||||||
env["RESTIC_REPOSITORY"] = "/repo"
|
env["RESTIC_REPOSITORY"] = "/repo"
|
||||||
|
case "b2":
|
||||||
|
case "s3":
|
||||||
|
// No additional setup needed
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Backend type \"%s\" is not supported as volume endpoint", b.Type)
|
||||||
}
|
}
|
||||||
for key, value := range env {
|
for key, value := range env {
|
||||||
docker = append(docker, "--env", key+"="+value)
|
docker = append(docker, "--env", key+"="+value)
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "1.4.0"
|
const VERSION = "1.4.1"
|
||||||
|
|
||||||
var CI bool = false
|
var CI bool = false
|
||||||
var VERBOSE bool = false
|
var VERBOSE bool = false
|
||||||
@ -55,6 +55,7 @@ func GetConfig() *Config {
|
|||||||
|
|
||||||
config = &Config{}
|
config = &Config{}
|
||||||
if err := viper.UnmarshalExact(config); err != nil {
|
if err := viper.UnmarshalExact(config); err != nil {
|
||||||
|
colors.Error.Println(err)
|
||||||
colors.Error.Println("Could not parse config file!")
|
colors.Error.Println("Could not parse config file!")
|
||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -81,7 +82,11 @@ func (c *Config) Describe() {
|
|||||||
var tmp string
|
var tmp string
|
||||||
colors.PrimaryPrint(`Location: "%s"`, name)
|
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 = ""
|
tmp = ""
|
||||||
for _, to := range l.To {
|
for _, to := range l.To {
|
||||||
@ -253,12 +258,7 @@ func appendOptionsToSlice(str *[]string, options OptionMap) {
|
|||||||
*str = append(*str, optionToString(key))
|
*str = append(*str, optionToString(key))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// String
|
*str = append(*str, optionToString(key), fmt.Sprint(value))
|
||||||
asString, ok := value.(string)
|
|
||||||
if ok {
|
|
||||||
*str = append(*str, optionToString(key), asString)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,3 +274,15 @@ func getOptions(options Options, key string) []string {
|
|||||||
}
|
}
|
||||||
return selected
|
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
|
||||||
|
}
|
||||||
|
@ -19,12 +19,12 @@ type LocationType string
|
|||||||
const (
|
const (
|
||||||
TypeLocal LocationType = "local"
|
TypeLocal LocationType = "local"
|
||||||
TypeVolume LocationType = "volume"
|
TypeVolume LocationType = "volume"
|
||||||
VolumePrefix string = "volume:"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HookArray = []string
|
type HookArray = []string
|
||||||
|
|
||||||
type Hooks struct {
|
type Hooks struct {
|
||||||
|
Dir string `yaml:"dir"`
|
||||||
Before HookArray `yaml:"before,omitempty"`
|
Before HookArray `yaml:"before,omitempty"`
|
||||||
After HookArray `yaml:"after,omitempty"`
|
After HookArray `yaml:"after,omitempty"`
|
||||||
Success HookArray `yaml:"success,omitempty"`
|
Success HookArray `yaml:"success,omitempty"`
|
||||||
@ -33,7 +33,8 @@ type Hooks struct {
|
|||||||
|
|
||||||
type Location struct {
|
type Location struct {
|
||||||
name string `yaml:",omitempty"`
|
name string `yaml:",omitempty"`
|
||||||
From string `yaml:"from,omitempty"`
|
From []string `yaml:"from,omitempty"`
|
||||||
|
Type string `yaml:"type,omitempty"`
|
||||||
To []string `yaml:"to,omitempty"`
|
To []string `yaml:"to,omitempty"`
|
||||||
Hooks Hooks `yaml:"hooks,omitempty"`
|
Hooks Hooks `yaml:"hooks,omitempty"`
|
||||||
Cron string `yaml:"cron,omitempty"`
|
Cron string `yaml:"cron,omitempty"`
|
||||||
@ -47,11 +48,17 @@ func GetLocation(name string) (Location, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) validate() error {
|
func (l Location) validate() error {
|
||||||
if l.From == "" {
|
if len(l.From) == 0 {
|
||||||
return fmt.Errorf(`Location "%s" is missing "from" key`, l.name)
|
return fmt.Errorf(`Location "%s" is missing "from" key`, l.name)
|
||||||
}
|
}
|
||||||
if l.getType() == TypeLocal {
|
t, err := l.getType()
|
||||||
if from, err := GetPathRelativeToConfig(l.From); err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case TypeLocal:
|
||||||
|
for _, path := range l.From {
|
||||||
|
if from, err := GetPathRelativeToConfig(path); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if stat, err := os.Stat(from); err != nil {
|
if stat, err := os.Stat(from); err != nil {
|
||||||
@ -63,6 +70,11 @@ func (l Location) validate() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 {
|
if len(l.To) == 0 {
|
||||||
return fmt.Errorf(`Location "%s" has no "to" targets`, l.name)
|
return fmt.Errorf(`Location "%s" has no "to" targets`, l.name)
|
||||||
@ -77,10 +89,17 @@ func (l Location) validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecuteHooks(commands []string, options ExecuteOptions) error {
|
func (l Location) ExecuteHooks(commands []string, options ExecuteOptions) error {
|
||||||
if len(commands) == 0 {
|
if len(commands) == 0 {
|
||||||
return nil
|
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")
|
colors.Secondary.Println("\nRunning hooks")
|
||||||
for _, command := range commands {
|
for _, command := range commands {
|
||||||
colors.Body.Println("> " + command)
|
colors.Body.Println("> " + command)
|
||||||
@ -97,39 +116,38 @@ func ExecuteHooks(commands []string, options ExecuteOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) getType() LocationType {
|
func (l Location) getType() (LocationType, error) {
|
||||||
if strings.HasPrefix(l.From, VolumePrefix) {
|
t := strings.ToLower(l.Type)
|
||||||
return TypeVolume
|
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 {
|
func (l Location) getTag(parts ...string) string {
|
||||||
return strings.TrimPrefix(l.From, VolumePrefix)
|
parts = append([]string{"ar"}, parts...)
|
||||||
|
return strings.Join(parts, ":")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) getPath() (string, error) {
|
func (l Location) getLocationTag() string {
|
||||||
t := l.getType()
|
return l.getTag("location", l.name)
|
||||||
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) Backup(cron bool, specificBackend string) []error {
|
func (l Location) Backup(cron bool, specificBackend string) []error {
|
||||||
var errors []error
|
var errors []error
|
||||||
var backends []string
|
var backends []string
|
||||||
colors.PrimaryPrint(" Backing up location \"%s\" ", l.name)
|
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{
|
options := ExecuteOptions{
|
||||||
Command: "bash",
|
Command: "bash",
|
||||||
|
Dir: cwd,
|
||||||
Envs: map[string]string{
|
Envs: map[string]string{
|
||||||
"AUTORESTIC_LOCATION": l.name,
|
"AUTORESTIC_LOCATION": l.name,
|
||||||
},
|
},
|
||||||
@ -137,18 +155,11 @@ func (l Location) Backup(cron bool, specificBackend string) []error {
|
|||||||
|
|
||||||
if err := l.validate(); err != nil {
|
if err := l.validate(); err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
colors.Error.Print(err)
|
|
||||||
goto after
|
goto after
|
||||||
}
|
}
|
||||||
|
|
||||||
if t == TypeLocal {
|
|
||||||
dir, _ := GetPathRelativeToConfig(l.From)
|
|
||||||
colors.Faint.Printf("Executing under: \"%s\"\n", dir)
|
|
||||||
options.Dir = dir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
if err := 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
|
||||||
}
|
}
|
||||||
@ -173,30 +184,38 @@ func (l Location) Backup(cron bool, specificBackend string) []error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
lFlags := getOptions(l.Options, "backup")
|
|
||||||
bFlags := getOptions(backend.Options, "backup")
|
|
||||||
cmd := []string{"backup"}
|
cmd := []string{"backup"}
|
||||||
cmd = append(cmd, lFlags...)
|
cmd = append(cmd, combineOptions("backup", l, backend)...)
|
||||||
cmd = append(cmd, bFlags...)
|
|
||||||
if cron {
|
if cron {
|
||||||
cmd = append(cmd, "--tag", "cron")
|
cmd = append(cmd, "--tag", l.getTag("cron"))
|
||||||
}
|
}
|
||||||
cmd = append(cmd, ".")
|
cmd = append(cmd, "--tag", l.getLocationTag())
|
||||||
backupOptions := ExecuteOptions{
|
backupOptions := ExecuteOptions{
|
||||||
Dir: options.Dir,
|
|
||||||
Envs: env,
|
Envs: env,
|
||||||
}
|
}
|
||||||
|
|
||||||
var out string
|
var out string
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case TypeLocal:
|
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...)
|
out, err = ExecuteResticCommand(backupOptions, cmd...)
|
||||||
case TypeVolume:
|
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)
|
out, err = backend.ExecDocker(l, cmd)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
colors.Error.Println(out)
|
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -213,7 +232,7 @@ func (l Location) Backup(cron bool, specificBackend string) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// After hooks
|
// 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)
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,22 +243,19 @@ after:
|
|||||||
} else {
|
} else {
|
||||||
commands = l.Hooks.Success
|
commands = l.Hooks.Success
|
||||||
}
|
}
|
||||||
if err := ExecuteHooks(commands, options); err != nil {
|
if err := l.ExecuteHooks(commands, options); err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(errors) == 0 {
|
||||||
colors.Success.Println("Done")
|
colors.Success.Println("Done")
|
||||||
|
}
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Forget(prune bool, dry bool) error {
|
func (l Location) Forget(prune bool, dry bool) error {
|
||||||
colors.PrimaryPrint("Forgetting for location \"%s\"", l.name)
|
colors.PrimaryPrint("Forgetting for location \"%s\"", l.name)
|
||||||
|
|
||||||
path, err := l.getPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, to := range l.To {
|
for _, to := range l.To {
|
||||||
backend, _ := GetBackend(to)
|
backend, _ := GetBackend(to)
|
||||||
colors.Secondary.Printf("For backend \"%s\"\n", backend.name)
|
colors.Secondary.Printf("For backend \"%s\"\n", backend.name)
|
||||||
@ -250,17 +266,14 @@ func (l Location) Forget(prune bool, dry bool) error {
|
|||||||
options := ExecuteOptions{
|
options := ExecuteOptions{
|
||||||
Envs: env,
|
Envs: env,
|
||||||
}
|
}
|
||||||
lFlags := getOptions(l.Options, "forget")
|
cmd := []string{"forget", "--tag", l.getLocationTag()}
|
||||||
bFlags := getOptions(backend.Options, "forget")
|
|
||||||
cmd := []string{"forget", "--path", path}
|
|
||||||
if prune {
|
if prune {
|
||||||
cmd = append(cmd, "--prune")
|
cmd = append(cmd, "--prune")
|
||||||
}
|
}
|
||||||
if dry {
|
if dry {
|
||||||
cmd = append(cmd, "--dry-run")
|
cmd = append(cmd, "--dry-run")
|
||||||
}
|
}
|
||||||
cmd = append(cmd, lFlags...)
|
cmd = append(cmd, combineOptions("forget", l, backend)...)
|
||||||
cmd = append(cmd, bFlags...)
|
|
||||||
out, err := ExecuteResticCommand(options, cmd...)
|
out, err := ExecuteResticCommand(options, cmd...)
|
||||||
if VERBOSE {
|
if VERBOSE {
|
||||||
colors.Faint.Println(out)
|
colors.Faint.Println(out)
|
||||||
@ -282,7 +295,7 @@ func (l Location) hasBackend(backend string) bool {
|
|||||||
return false
|
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 == "" {
|
if from == "" {
|
||||||
from = l.To[0]
|
from = l.To[0]
|
||||||
} else if !l.hasBackend(from) {
|
} else if !l.hasBackend(from) {
|
||||||
@ -293,16 +306,20 @@ func (l Location) Restore(to, from string, force bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
colors.PrimaryPrint("Restoring location \"%s\"", l.name)
|
|
||||||
|
|
||||||
backend, _ := GetBackend(from)
|
if snapshot == "" {
|
||||||
path, err := l.getPath()
|
snapshot = "latest"
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
colors.Secondary.Println("Restoring lastest snapshot")
|
|
||||||
colors.Body.Printf("%s → %s.\n", from, path)
|
colors.PrimaryPrint("Restoring location \"%s\"", l.name)
|
||||||
switch l.getType() {
|
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:
|
case TypeLocal:
|
||||||
// Check if target is empty
|
// Check if target is empty
|
||||||
if !force {
|
if !force {
|
||||||
@ -322,9 +339,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.getLocationTag(), snapshot})
|
||||||
case TypeVolume:
|
case TypeVolume:
|
||||||
_, err = backend.ExecDocker(l, []string{"restore", "--target", ".", "--path", path, "latest"})
|
_, err = backend.ExecDocker(l, []string{"restore", "--target", "/", "--tag", l.getLocationTag(), snapshot})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -77,3 +77,9 @@ func CopyFile(from, to string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckIfVolumeExists(volume string) bool {
|
||||||
|
out, err := ExecuteCommand(ExecuteOptions{Command: "docker"}, "volume", "inspect", volume)
|
||||||
|
fmt.Println(out)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user