diff --git a/cmd/backup.go b/cmd/backup.go index 81f728d..a966da9 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -30,9 +30,7 @@ var backupCmd = &cobra.Command{ CheckErr(err) defer lock.Unlock() - config := internal.GetConfig() - err = config.CheckConfig() - CheckErr(err) + CheckErr(internal.CheckConfig()) selected, err := internal.GetAllOrSelected(cmd, false) CheckErr(err) diff --git a/cmd/check.go b/cmd/check.go index 292cd0e..c629d0e 100644 --- a/cmd/check.go +++ b/cmd/check.go @@ -31,9 +31,8 @@ var checkCmd = &cobra.Command{ CheckErr(err) defer lock.Unlock() - config := internal.GetConfig() - err = config.CheckConfig() - CheckErr(err) + CheckErr(internal.CheckConfig()) + colors.Success.Println("Everyting is fine.") }, } diff --git a/cmd/exec.go b/cmd/exec.go index 4d5382e..6cfe4b8 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -31,9 +31,7 @@ var execCmd = &cobra.Command{ CheckErr(err) defer lock.Unlock() - config := internal.GetConfig() - err = config.CheckConfig() - CheckErr(err) + CheckErr(internal.CheckConfig()) selected, err := internal.GetAllOrSelected(cmd, true) CheckErr(err) diff --git a/cmd/forget.go b/cmd/forget.go index f8a822a..a3a3060 100644 --- a/cmd/forget.go +++ b/cmd/forget.go @@ -30,8 +30,7 @@ var forgetCmd = &cobra.Command{ CheckErr(err) defer lock.Unlock() - config := internal.GetConfig() - CheckErr(config.CheckConfig()) + CheckErr(internal.CheckConfig()) selected, err := internal.GetAllOrSelected(cmd, false) CheckErr(err) diff --git a/internal/backend.go b/internal/backend.go index 1d7594d..a0bdbd5 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -12,21 +12,17 @@ import ( ) type Backend struct { - Name string `mapstructure:"name"` - Type string `mapstructure:"type"` - Path string `mapstructure:"path"` - Key string `mapstructure:"key"` - Env map[string]string `mapstructure:"env"` + name string + Type string `mapstructure:"type,omitempty"` + Path string `mapstructure:"path,omitempty"` + Key string `mapstructure:"key,omitempty"` + Env map[string]string `mapstructure:"env,omitempty"` } func GetBackend(name string) (Backend, bool) { - c := GetConfig() - for _, b := range c.Backends { - if b.Name == name { - return b, true - } - } - return Backend{}, false + b, ok := GetConfig().Backends[name] + b.name = name + return b, ok } func (b Backend) generateRepo() (string, error) { @@ -59,25 +55,20 @@ func generateRandomKey() string { } func (b Backend) validate() error { - if b.Name == "" { - return fmt.Errorf(`Backend has no "name"`) - } if b.Type == "" { - return fmt.Errorf(`Backend "%s" has no "type"`, b.Name) + return fmt.Errorf(`Backend "%s" has no "type"`, b.name) } if b.Path == "" { - return fmt.Errorf(`Backend "%s" has no "path"`, b.Name) + return fmt.Errorf(`Backend "%s" has no "path"`, b.name) } if b.Key == "" { key := generateRandomKey() b.Key = key c := GetConfig() - for i, backend := range c.Backends { - if backend.Name == b.Name { - c.Backends[i].Key = key - break - } - } + tmp := c.Backends[b.name] + tmp.Key = key + tmp.name = "" + c.Backends[b.name] = tmp file := viper.ConfigFileUsed() if err := CopyFile(file, file+".old"); err != nil { return err diff --git a/internal/config.go b/internal/config.go index 57f24b0..10cb2d4 100644 --- a/internal/config.go +++ b/internal/config.go @@ -18,8 +18,8 @@ var CI bool = false var VERBOSE bool = false type Config struct { - Locations []Location `mapstructure:"locations"` - Backends []Backend `mapstructure:"backends"` + Locations map[string]Location `mapstructure:"locations"` + Backends map[string]Backend `mapstructure:"backends"` } var once sync.Once @@ -31,7 +31,6 @@ func GetConfig() *Config { if err := viper.ReadInConfig(); err == nil { colors.Faint.Println("Using config file:", viper.ConfigFileUsed()) } else { - return } @@ -55,34 +54,25 @@ func GetPathRelativeToConfig(p string) (string, error) { } } -func (c *Config) CheckConfig() error { +func CheckConfig() error { + c := GetConfig() if c == nil { return fmt.Errorf("config could not be loaded/found") } if !CheckIfResticIsCallable() { return fmt.Errorf(`restic was not found. Install either with "autorestic install" or manually`) } - found := map[string]bool{} - for _, backend := range c.Backends { + for name, backend := range c.Backends { + backend.name = name if err := backend.validate(); err != nil { return err } - if _, ok := found[backend.Name]; ok { - return fmt.Errorf(`duplicate name for backends "%s"`, backend.Name) - } else { - found[backend.Name] = true - } } - found = map[string]bool{} - for _, location := range c.Locations { + for name, location := range c.Locations { + location.name = name if err := location.validate(c); err != nil { return err } - if _, ok := found[location.Name]; ok { - return fmt.Errorf(`duplicate name for locations "%s"`, location.Name) - } else { - found[location.Name] = true - } } return nil } @@ -90,47 +80,47 @@ func (c *Config) CheckConfig() error { func GetAllOrSelected(cmd *cobra.Command, backends bool) ([]string, error) { var list []string if backends { - for _, b := range config.Backends { - list = append(list, b.Name) + for name := range config.Backends { + list = append(list, name) } } else { - for _, l := range config.Locations { - list = append(list, l.Name) + for name := range config.Locations { + list = append(list, name) } } + all, _ := cmd.Flags().GetBool("all") if all { return list, nil + } + + var selected []string + if backends { + selected, _ = cmd.Flags().GetStringSlice("backend") } else { - var selected []string - if backends { - tmp, _ := cmd.Flags().GetStringSlice("backend") - selected = tmp - } else { - tmp, _ := cmd.Flags().GetStringSlice("location") - selected = tmp - } - for _, s := range selected { - found := false - for _, l := range list { - if l == s { - found = true - break - } - } - if !found { - if backends { - return nil, fmt.Errorf("invalid backend \"%s\"", s) - } else { - return nil, fmt.Errorf("invalid location \"%s\"", s) - } + selected, _ = cmd.Flags().GetStringSlice("location") + } + for _, s := range selected { + found := false + for _, l := range list { + if l == s { + found = true + break } } - if len(selected) == 0 { - return selected, fmt.Errorf("nothing selected, aborting") + if !found { + if backends { + return nil, fmt.Errorf("invalid backend \"%s\"", s) + } else { + return nil, fmt.Errorf("invalid location \"%s\"", s) + } } - return selected, nil } + + if len(selected) == 0 { + return selected, fmt.Errorf("nothing selected, aborting") + } + return selected, nil } func AddFlagsToCommand(cmd *cobra.Command, backend bool) { diff --git a/internal/cron.go b/internal/cron.go index f1bf6be..56ae3d5 100644 --- a/internal/cron.go +++ b/internal/cron.go @@ -2,7 +2,8 @@ package internal func RunCron() error { c := GetConfig() - for _, l := range c.Locations { + for name, l := range c.Locations { + l.name = name if err := l.RunCron(); err != nil { return err } diff --git a/internal/location.go b/internal/location.go index 756643b..4a2ecd7 100644 --- a/internal/location.go +++ b/internal/location.go @@ -31,33 +31,26 @@ type Hooks struct { type Options map[string]map[string][]string type Location struct { - Name string `mapstructure:"name"` - From string `mapstructure:"from"` - To []string `mapstructure:"to"` - Hooks Hooks `mapstructure:"hooks"` - Cron string `mapstructure:"cron"` - Options Options `mapstructure:"options"` + name string `mapstructure:",omitempty"` + From string `mapstructure:"from,omitempty"` + To []string `mapstructure:"to,omitempty"` + Hooks Hooks `mapstructure:"hooks,omitempty"` + Cron string `mapstructure:"cron,omitempty"` + Options Options `mapstructure:"options,omitempty"` } func GetLocation(name string) (Location, bool) { - c := GetConfig() - for _, b := range c.Locations { - if b.Name == name { - return b, true - } - } - return Location{}, false + l, ok := GetConfig().Locations[name] + l.name = name + return l, ok } func (l Location) validate(c *Config) error { - if l.Name == "" { - return fmt.Errorf(`Location is missing name`) - } if l.From == "" { - return fmt.Errorf(`Location "%s" is missing "from" key`, l.Name) + return fmt.Errorf(`Location "%s" is missing "from" key`, l.name) } 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 { @@ -120,13 +113,13 @@ func (l Location) getPath() (string, error) { return path, nil } case TypeVolume: - return "/volume/" + l.Name + "/" + l.getVolumeName(), nil + return "/volume/" + l.name + "/" + l.getVolumeName(), nil } - 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() error { - colors.PrimaryPrint(" Backing up location \"%s\" ", l.Name) + colors.PrimaryPrint(" Backing up location \"%s\" ", l.name) t := l.getType() options := ExecuteOptions{ Command: "bash", @@ -145,7 +138,7 @@ func (l Location) Backup() error { // Backup for _, to := range l.To { backend, _ := GetBackend(to) - colors.Secondary.Printf("Backend: %s\n", backend.Name) + colors.Secondary.Printf("Backend: %s\n", backend.name) env, err := backend.getEnv() if err != nil { return nil @@ -186,7 +179,7 @@ func (l Location) Backup() 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 { @@ -195,7 +188,7 @@ func (l Location) Forget(prune bool, dry bool) error { for _, to := range l.To { backend, _ := GetBackend(to) - colors.Secondary.Printf("For backend \"%s\"\n", backend.Name) + colors.Secondary.Printf("For backend \"%s\"\n", backend.name) env, err := backend.getEnv() if err != nil { return nil @@ -244,7 +237,7 @@ func (l Location) Restore(to, from string, force bool) error { if err != nil { return err } - colors.PrimaryPrint("Restoring location \"%s\"", l.Name) + colors.PrimaryPrint("Restoring location \"%s\"", l.name) backend, _ := GetBackend(from) path, err := l.getPath() @@ -293,14 +286,14 @@ func (l Location) RunCron() error { if err != nil { return err } - last := time.Unix(lock.GetCron(l.Name), 0) + last := time.Unix(lock.GetCron(l.name), 0) next := schedule.Next(last) now := time.Now() if now.After(next) { - lock.SetCron(l.Name, now.Unix()) + lock.SetCron(l.name, now.Unix()) l.Backup() } else { - colors.Body.Printf("Skipping \"%s\", not due yet.\n", l.Name) + colors.Body.Printf("Skipping \"%s\", not due yet.\n", l.name) } return nil }