diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index 08fc2387d..d00f7dbc5 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -3,10 +3,8 @@ package main import ( "context" "fmt" - "os" "github.com/restic/restic/internal/debug" - "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/restic" "github.com/spf13/cobra" @@ -29,14 +27,10 @@ their deduplication. // CopyOptions bundles all options for the copy command. type CopyOptions struct { - Repo string - password string - PasswordFile string - PasswordCommand string - KeyHint string - Hosts []string - Tags restic.TagLists - Paths []string + secondaryRepoOptions + Hosts []string + Tags restic.TagLists + Paths []string } var copyOptions CopyOptions @@ -45,35 +39,14 @@ func init() { cmdRoot.AddCommand(cmdCopy) f := cmdCopy.Flags() - f.StringVarP(©Options.Repo, "repo2", "", os.Getenv("RESTIC_REPOSITORY2"), "destination repository to copy snapshots to (default: $RESTIC_REPOSITORY2)") - f.StringVarP(©Options.PasswordFile, "password-file2", "", os.Getenv("RESTIC_PASSWORD_FILE2"), "`file` to read the destination repository password from (default: $RESTIC_PASSWORD_FILE2)") - f.StringVarP(©Options.KeyHint, "key-hint2", "", os.Getenv("RESTIC_KEY_HINT2"), "key ID of key to try decrypting the destination repository first (default: $RESTIC_KEY_HINT2)") - f.StringVarP(©Options.PasswordCommand, "password-command2", "", os.Getenv("RESTIC_PASSWORD_COMMAND2"), "shell `command` to obtain the destination repository password from (default: $RESTIC_PASSWORD_COMMAND2)") - + initSecondaryRepoOptions(f, ©Options.secondaryRepoOptions, "destination", "to copy snapshots to") f.StringArrayVarP(©Options.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when no snapshot ID is given (can be specified multiple times)") f.Var(©Options.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given") f.StringArrayVar(©Options.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given") } func runCopy(opts CopyOptions, gopts GlobalOptions, args []string) error { - if opts.Repo == "" { - return errors.Fatal("Please specify a destination repository location (--repo2)") - } - var err error - dstGopts := gopts - dstGopts.Repo = opts.Repo - dstGopts.PasswordFile = opts.PasswordFile - dstGopts.PasswordCommand = opts.PasswordCommand - dstGopts.KeyHint = opts.KeyHint - if opts.password != "" { - dstGopts.password = opts.password - } else { - dstGopts.password, err = resolvePassword(dstGopts, "RESTIC_PASSWORD2") - if err != nil { - return err - } - } - dstGopts.password, err = ReadPassword(dstGopts, "enter password for destination repository: ") + dstGopts, err := fillSecondaryGlobalOpts(opts.secondaryRepoOptions, gopts, "destination") if err != nil { return err } diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index b279a75b0..deba04586 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -616,8 +616,10 @@ func TestBackupTags(t *testing.T) { func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) { copyOpts := CopyOptions{ - Repo: dstGopts.Repo, - password: dstGopts.password, + secondaryRepoOptions: secondaryRepoOptions{ + Repo: dstGopts.Repo, + password: dstGopts.password, + }, } rtest.OK(t, runCopy(copyOpts, srcGopts, nil)) diff --git a/cmd/restic/secondary_repo.go b/cmd/restic/secondary_repo.go new file mode 100644 index 000000000..b49cd95c8 --- /dev/null +++ b/cmd/restic/secondary_repo.go @@ -0,0 +1,48 @@ +package main + +import ( + "os" + + "github.com/restic/restic/internal/errors" + "github.com/spf13/pflag" +) + +type secondaryRepoOptions struct { + Repo string + password string + PasswordFile string + PasswordCommand string + KeyHint string +} + +func initSecondaryRepoOptions(f *pflag.FlagSet, opts *secondaryRepoOptions, repoPrefix string, repoUsage string) { + f.StringVarP(&opts.Repo, "repo2", "", os.Getenv("RESTIC_REPOSITORY2"), repoPrefix+" repository "+repoUsage+" (default: $RESTIC_REPOSITORY2)") + f.StringVarP(&opts.PasswordFile, "password-file2", "", os.Getenv("RESTIC_PASSWORD_FILE2"), "`file` to read the "+repoPrefix+" repository password from (default: $RESTIC_PASSWORD_FILE2)") + f.StringVarP(&opts.KeyHint, "key-hint2", "", os.Getenv("RESTIC_KEY_HINT2"), "key ID of key to try decrypting the "+repoPrefix+" repository first (default: $RESTIC_KEY_HINT2)") + f.StringVarP(&opts.PasswordCommand, "password-command2", "", os.Getenv("RESTIC_PASSWORD_COMMAND2"), "shell `command` to obtain the "+repoPrefix+" repository password from (default: $RESTIC_PASSWORD_COMMAND2)") +} + +func fillSecondaryGlobalOpts(opts secondaryRepoOptions, gopts GlobalOptions, repoPrefix string) (GlobalOptions, error) { + if opts.Repo == "" { + return GlobalOptions{}, errors.Fatal("Please specify a " + repoPrefix + " repository location (--repo2)") + } + var err error + dstGopts := gopts + dstGopts.Repo = opts.Repo + dstGopts.PasswordFile = opts.PasswordFile + dstGopts.PasswordCommand = opts.PasswordCommand + dstGopts.KeyHint = opts.KeyHint + if opts.password != "" { + dstGopts.password = opts.password + } else { + dstGopts.password, err = resolvePassword(dstGopts, "RESTIC_PASSWORD2") + if err != nil { + return GlobalOptions{}, err + } + } + dstGopts.password, err = ReadPassword(dstGopts, "enter password for "+repoPrefix+" repository: ") + if err != nil { + return GlobalOptions{}, err + } + return dstGopts, nil +}