From 5418f56b1a60f573a72b620ab63cbf46e9988120 Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Wed, 13 Apr 2022 17:07:38 +0200 Subject: [PATCH] add copy option --- internal/backend.go | 16 ++++++------- internal/config.go | 10 ++++---- internal/location.go | 54 ++++++++++++++++++++++++++++++++++++++++++-- internal/utils.go | 9 ++++++++ 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/internal/backend.go b/internal/backend.go index b90d7ce..745b800 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -13,18 +13,18 @@ import ( ) type BackendRest struct { - User string `yaml:"user,omitempty"` - Password string `yaml:"password,omitempty"` + User string `mapstructure:"user,omitempty"` + Password string `mapstructure:"password,omitempty"` } type Backend struct { name string - Type string `yaml:"type,omitempty"` - Path string `yaml:"path,omitempty"` - Key string `yaml:"key,omitempty"` - Env map[string]string `yaml:"env,omitempty"` - Rest BackendRest `yaml:"rest,omitempty"` - Options Options `yaml:"options,omitempty"` + Type string `mapstructure:"type,omitempty"` + Path string `mapstructure:"path,omitempty"` + Key string `mapstructure:"key,omitempty"` + Env map[string]string `mapstructure:"env,omitempty"` + Rest BackendRest `mapstructure:"rest,omitempty"` + Options Options `mapstructure:"options,omitempty"` } func GetBackend(name string) (Backend, bool) { diff --git a/internal/config.go b/internal/config.go index 469f87b..26c584c 100644 --- a/internal/config.go +++ b/internal/config.go @@ -23,11 +23,11 @@ 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"` + Version string `mapstructure:"version"` + Extras interface{} `mapstructure:"extras"` + Locations map[string]Location `mapstructure:"locations"` + Backends map[string]Backend `mapstructure:"backends"` + Global Options `mapstructure:"global"` } var once sync.Once diff --git a/internal/location.go b/internal/location.go index 9864cf1..b715105 100644 --- a/internal/location.go +++ b/internal/location.go @@ -40,6 +40,8 @@ type Hooks struct { Failure HookArray `mapstructure:"failure,omitempty"` } +type LocationCopy = map[string][]string + type Location struct { name string `mapstructure:",omitempty"` From []string `mapstructure:"from,omitempty"` @@ -49,6 +51,7 @@ type Location struct { Cron string `mapstructure:"cron,omitempty"` Options Options `mapstructure:"options,omitempty"` ForgetOption LocationForgetOption `mapstructure:"forget,omitempty"` + CopyOption LocationCopy `mapstructure:"copy,omitempty"` } func GetLocation(name string) (Location, bool) { @@ -87,15 +90,34 @@ func (l Location) validate() error { } 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) } // Check if backends are all valid for _, to := range l.To { _, ok := GetBackend(to) if !ok { - return fmt.Errorf("invalid backend `%s`", to) + return fmt.Errorf(`location "%s" has an invalid backend "%s"`, l.name, to) } } + + // Check copy option + for copyFrom, copyTo := range l.CopyOption { + if _, ok := GetBackend(copyFrom); !ok { + return fmt.Errorf(`location "%s" has an invalid backend "%s" in copy option`, l.name, copyFrom) + } + if !ArrayContains(l.To, copyFrom) { + return fmt.Errorf(`location "%s" has an invalid copy from "%s"`, l.name, copyFrom) + } + for _, copyToTarget := range copyTo { + if _, ok := GetBackend(copyToTarget); !ok { + return fmt.Errorf(`location "%s" has an invalid backend "%s" in copy option`, l.name, copyToTarget) + } + if ArrayContains(l.To, copyToTarget) { + return fmt.Errorf(`location "%s" cannot copy to "%s" as it's already a target`, l.name, copyToTarget) + } + } + } + // Check if forget type is correct if l.ForgetOption != "" { if l.ForgetOption != LocationForgetYes && l.ForgetOption != LocationForgetNo && l.ForgetOption != LocationForgetPrune { @@ -249,6 +271,34 @@ func (l Location) Backup(cron bool, specificBackend string) []error { continue } + // Copy + if md.SnapshotID != "" { + for copyFrom, copyTo := range l.CopyOption { + b1, _ := GetBackend(copyFrom) + for _, copyToTarget := range copyTo { + b2, _ := GetBackend(copyToTarget) + colors.Secondary.Println("Copying " + copyFrom + " → " + copyToTarget) + env, _ := b1.getEnv() + env2, _ := b2.getEnv() + // Add the second repo to the env with a "2" suffix + for k, v := range env2 { + env[k+"2"] = v + } + _, out, err := ExecuteResticCommand(ExecuteOptions{ + Envs: env, + }, "copy", md.SnapshotID) + + if flags.VERBOSE { + colors.Faint.Println(out) + } + + if err != nil { + errors = append(errors, err) + } + } + } + } + if flags.VERBOSE { colors.Faint.Println(out) } diff --git a/internal/utils.go b/internal/utils.go index 8f12077..42389f1 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -87,3 +87,12 @@ func CheckIfVolumeExists(volume string) bool { _, _, err := ExecuteCommand(ExecuteOptions{Command: "docker"}, "volume", "inspect", volume) return err == nil } + +func ArrayContains[T comparable](arr []T, needle T) bool { + for _, item := range arr { + if item == needle { + return true + } + } + return false +}