diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index 11b093657..e216d8258 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -70,7 +70,8 @@ func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) { } func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string, term *termstatus.Terminal) error { - secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination") + printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination", printer) if err != nil { return err } @@ -79,8 +80,6 @@ func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args [] gopts, secondaryGopts = secondaryGopts, gopts } - printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) - ctx, srcRepo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer) if err != nil { return err diff --git a/cmd/restic/cmd_init.go b/cmd/restic/cmd_init.go index 0ac65dbe4..9fabaf5d4 100644 --- a/cmd/restic/cmd_init.go +++ b/cmd/restic/cmd_init.go @@ -10,6 +10,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" + "github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" @@ -79,7 +80,7 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args [] return errors.Fatalf("only repository versions between %v and %v are allowed", restic.MinRepoVersion, restic.MaxRepoVersion) } - chunkerPolynomial, err := maybeReadChunkerPolynomial(ctx, opts, gopts) + chunkerPolynomial, err := maybeReadChunkerPolynomial(ctx, opts, gopts, printer) if err != nil { return err } @@ -91,12 +92,13 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args [] gopts.password, err = ReadPasswordTwice(ctx, gopts, "enter password for new repository: ", - "enter password again: ") + "enter password again: ", + printer) if err != nil { return err } - be, err := create(ctx, gopts.Repo, gopts, gopts.extended) + be, err := create(ctx, gopts.Repo, gopts, gopts.extended, printer) if err != nil { return errors.Fatalf("create repository at %s failed: %v\n", location.StripPassword(gopts.backends, gopts.Repo), err) } @@ -136,14 +138,14 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args [] return nil } -func maybeReadChunkerPolynomial(ctx context.Context, opts InitOptions, gopts GlobalOptions) (*chunker.Pol, error) { +func maybeReadChunkerPolynomial(ctx context.Context, opts InitOptions, gopts GlobalOptions, printer progress.Printer) (*chunker.Pol, error) { if opts.CopyChunkerParameters { - otherGopts, _, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "secondary") + otherGopts, _, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "secondary", printer) if err != nil { return nil, err } - otherRepo, err := OpenRepository(ctx, otherGopts) + otherRepo, err := OpenRepository(ctx, otherGopts, printer) if err != nil { return nil, err } diff --git a/cmd/restic/cmd_init_integration_test.go b/cmd/restic/cmd_init_integration_test.go index 3d4fddcf6..245ad1003 100644 --- a/cmd/restic/cmd_init_integration_test.go +++ b/cmd/restic/cmd_init_integration_test.go @@ -9,6 +9,7 @@ import ( "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/termstatus" ) @@ -54,10 +55,10 @@ func TestInitCopyChunkerParams(t *testing.T) { }) rtest.OK(t, err) - repo, err := OpenRepository(context.TODO(), env.gopts) + repo, err := OpenRepository(context.TODO(), env.gopts, &progress.NoopPrinter{}) rtest.OK(t, err) - otherRepo, err := OpenRepository(context.TODO(), env2.gopts) + otherRepo, err := OpenRepository(context.TODO(), env2.gopts, &progress.NoopPrinter{}) rtest.OK(t, err) rtest.Assert(t, repo.Config().ChunkerPolynomial == otherRepo.Config().ChunkerPolynomial, diff --git a/cmd/restic/cmd_key_add.go b/cmd/restic/cmd_key_add.go index dc07d6889..6842c154d 100644 --- a/cmd/restic/cmd_key_add.go +++ b/cmd/restic/cmd_key_add.go @@ -72,7 +72,7 @@ func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, arg } func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyAddOptions, printer progress.Printer) error { - pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword) + pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword, printer) if err != nil { return err } @@ -95,7 +95,7 @@ func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOption // testKeyNewPassword is used to set a new password during integration testing. var testKeyNewPassword string -func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile string, insecureNoPassword bool) (string, error) { +func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile string, insecureNoPassword bool, printer progress.Printer) (string, error) { if testKeyNewPassword != "" { return testKeyNewPassword, nil } @@ -127,7 +127,8 @@ func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile st return ReadPasswordTwice(ctx, newopts, "enter new password: ", - "enter password again: ") + "enter password again: ", + printer) } func switchToNewKeyAndRemoveIfBroken(ctx context.Context, repo *repository.Repository, key *repository.Key, pw string) error { diff --git a/cmd/restic/cmd_key_integration_test.go b/cmd/restic/cmd_key_integration_test.go index 95e5a912d..76ed214c9 100644 --- a/cmd/restic/cmd_key_integration_test.go +++ b/cmd/restic/cmd_key_integration_test.go @@ -12,6 +12,7 @@ import ( "github.com/restic/restic/internal/backend" "github.com/restic/restic/internal/repository" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui/progress" "github.com/restic/restic/internal/ui/termstatus" ) @@ -61,7 +62,7 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) { Hostname: "example.com", }, []string{}, term)) - repo, err := OpenRepository(context.TODO(), gopts) + repo, err := OpenRepository(context.TODO(), gopts, &progress.NoopPrinter{}) rtest.OK(t, err) key, err := repository.SearchKey(context.TODO(), repo, testKeyNewPassword, 2, "") rtest.OK(t, err) diff --git a/cmd/restic/cmd_key_passwd.go b/cmd/restic/cmd_key_passwd.go index 8e3be9ed4..7ccbc9903 100644 --- a/cmd/restic/cmd_key_passwd.go +++ b/cmd/restic/cmd_key_passwd.go @@ -67,7 +67,7 @@ func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOption } func changePassword(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyPasswdOptions, printer progress.Printer) error { - pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword) + pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword, printer) if err != nil { return err } diff --git a/cmd/restic/cmd_unlock.go b/cmd/restic/cmd_unlock.go index decea3a3b..49e802c1f 100644 --- a/cmd/restic/cmd_unlock.go +++ b/cmd/restic/cmd_unlock.go @@ -47,7 +47,7 @@ func (opts *UnlockOptions) AddFlags(f *pflag.FlagSet) { func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions, term *termstatus.Terminal) error { printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) - repo, err := OpenRepository(ctx, gopts) + repo, err := OpenRepository(ctx, gopts, printer) if err != nil { return err } diff --git a/cmd/restic/global.go b/cmd/restic/global.go index df9461272..b4dc64475 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -34,6 +34,7 @@ import ( "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/terminal" "github.com/restic/restic/internal/textfile" + "github.com/restic/restic/internal/ui/progress" "github.com/spf13/pflag" "github.com/restic/restic/internal/errors" @@ -293,7 +294,7 @@ func readPassword(in io.Reader) (password string, err error) { // ReadPassword reads the password from a password file, the environment // variable RESTIC_PASSWORD or prompts the user. If the context is canceled, // the function leaks the password reading goroutine. -func ReadPassword(ctx context.Context, opts GlobalOptions, prompt string) (string, error) { +func ReadPassword(ctx context.Context, opts GlobalOptions, prompt string, printer progress.Printer) (string, error) { if opts.InsecureNoPassword { if opts.password != "" { return "", errors.Fatal("--insecure-no-password must not be specified together with providing a password via a cli option or environment variable") @@ -313,10 +314,10 @@ func ReadPassword(ctx context.Context, opts GlobalOptions, prompt string) (strin if terminal.StdinIsTerminal() { password, err = terminal.ReadPassword(ctx, os.Stdin, os.Stderr, prompt) } else { - password, err = readPassword(os.Stdin) if terminal.StdoutIsTerminal() { - Verbosef("reading repository password from stdin\n") + printer.P("reading repository password from stdin") } + password, err = readPassword(os.Stdin) } if err != nil { @@ -333,13 +334,13 @@ func ReadPassword(ctx context.Context, opts GlobalOptions, prompt string) (strin // ReadPasswordTwice calls ReadPassword two times and returns an error when the // passwords don't match. If the context is canceled, the function leaks the // password reading goroutine. -func ReadPasswordTwice(ctx context.Context, gopts GlobalOptions, prompt1, prompt2 string) (string, error) { - pw1, err := ReadPassword(ctx, gopts, prompt1) +func ReadPasswordTwice(ctx context.Context, gopts GlobalOptions, prompt1, prompt2 string, printer progress.Printer) (string, error) { + pw1, err := ReadPassword(ctx, gopts, prompt1, printer) if err != nil { return "", err } if terminal.StdinIsTerminal() { - pw2, err := ReadPassword(ctx, gopts, prompt2) + pw2, err := ReadPassword(ctx, gopts, prompt2, printer) if err != nil { return "", err } @@ -380,13 +381,13 @@ func ReadRepo(opts GlobalOptions) (string, error) { const maxKeys = 20 // OpenRepository reads the password and opens the repository. -func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Repository, error) { +func OpenRepository(ctx context.Context, opts GlobalOptions, printer progress.Printer) (*repository.Repository, error) { repo, err := ReadRepo(opts) if err != nil { return nil, err } - be, err := open(ctx, repo, opts, opts.extended) + be, err := open(ctx, repo, opts, opts.extended, printer) if err != nil { return nil, err } @@ -406,13 +407,13 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi } for ; passwordTriesLeft > 0; passwordTriesLeft-- { - opts.password, err = ReadPassword(ctx, opts, "enter password for repository: ") + opts.password, err = ReadPassword(ctx, opts, "enter password for repository: ", printer) if ctx.Err() != nil { return nil, ctx.Err() } if err != nil && passwordTriesLeft > 1 { opts.password = "" - fmt.Printf("%s. Try again\n", err) + printer.E("%s. Try again", err) } if err != nil { continue @@ -421,7 +422,7 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi err = s.SearchKey(ctx, opts.password, maxKeys, opts.KeyHint) if err != nil && passwordTriesLeft > 1 { opts.password = "" - fmt.Fprintf(os.Stderr, "%s. Try again\n", err) + printer.E("%s. Try again", err) } } if err != nil { @@ -441,7 +442,7 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi if s.Config().Version >= 2 { extra = ", compression level " + opts.Compression.String() } - Verbosef("repository %v opened (version %v%s)\n", id, s.Config().Version, extra) + printer.P("repository %v opened (version %v%s)", id, s.Config().Version, extra) } } @@ -451,12 +452,12 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi c, err := cache.New(s.Config().ID, opts.CacheDir) if err != nil { - Warnf("unable to open cache: %v\n", err) + printer.E("unable to open cache: %v", err) return s, nil } if c.Created && !opts.JSON && terminal.StdoutIsTerminal() { - Verbosef("created new cache in %v\n", c.Base) + printer.P("created new cache in %v", c.Base) } // start using the cache @@ -464,7 +465,7 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi oldCacheDirs, err := cache.Old(c.Base) if err != nil { - Warnf("unable to find old cache directories: %v", err) + printer.E("unable to find old cache directories: %v", err) } // nothing more to do if no old cache dirs could be found @@ -475,18 +476,18 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi // cleanup old cache dirs if instructed to do so if opts.CleanupCache { if terminal.StdoutIsTerminal() && !opts.JSON { - Verbosef("removing %d old cache dirs from %v\n", len(oldCacheDirs), c.Base) + printer.P("removing %d old cache dirs from %v", len(oldCacheDirs), c.Base) } for _, item := range oldCacheDirs { dir := filepath.Join(c.Base, item.Name()) err = os.RemoveAll(dir) if err != nil { - Warnf("unable to remove %v: %v\n", dir, err) + printer.E("unable to remove %v: %v", dir, err) } } } else { if terminal.StdoutIsTerminal() { - Verbosef("found %d old cache directories in %v, run `restic cache --cleanup` to remove them\n", + printer.P("found %d old cache directories in %v, run `restic cache --cleanup` to remove them", len(oldCacheDirs), c.Base) } } @@ -510,7 +511,7 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro return cfg, nil } -func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, create bool) (backend.Backend, error) { +func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, create bool, printer progress.Printer) (backend.Backend, error) { debug.Log("parsing location %v", location.StripPassword(gopts.backends, s)) loc, err := location.Parse(gopts.backends, s) if err != nil { @@ -563,13 +564,13 @@ func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options. report := func(msg string, err error, d time.Duration) { if d >= 0 { - Warnf("%v returned error, retrying after %v: %v\n", msg, d, err) + printer.E("%v returned error, retrying after %v: %v", msg, d, err) } else { - Warnf("%v failed: %v\n", msg, err) + printer.E("%v failed: %v", msg, err) } } success := func(msg string, retries int) { - Warnf("%v operation successful after %d retries\n", msg, retries) + printer.E("%v operation successful after %d retries", msg, retries) } be = retry.New(be, 15*time.Minute, report, success) @@ -585,8 +586,8 @@ func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options. } // Open the backend specified by a location config. -func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Options) (backend.Backend, error) { - be, err := innerOpen(ctx, s, gopts, opts, false) +func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, printer progress.Printer) (backend.Backend, error) { + be, err := innerOpen(ctx, s, gopts, opts, false, printer) if err != nil { return nil, err } @@ -608,6 +609,6 @@ func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Optio } // Create the backend specified by URI. -func create(ctx context.Context, s string, gopts GlobalOptions, opts options.Options) (backend.Backend, error) { - return innerOpen(ctx, s, gopts, opts, true) +func create(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, printer progress.Printer) (backend.Backend, error) { + return innerOpen(ctx, s, gopts, opts, true, printer) } diff --git a/cmd/restic/global_test.go b/cmd/restic/global_test.go index 884476614..d25ff07c1 100644 --- a/cmd/restic/global_test.go +++ b/cmd/restic/global_test.go @@ -9,6 +9,7 @@ import ( "github.com/restic/restic/internal/errors" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui/progress" ) func Test_PrintFunctionsRespectsGlobalStdout(t *testing.T) { @@ -66,11 +67,11 @@ func TestReadRepo(t *testing.T) { func TestReadEmptyPassword(t *testing.T) { opts := GlobalOptions{InsecureNoPassword: true} - password, err := ReadPassword(context.TODO(), opts, "test") + password, err := ReadPassword(context.TODO(), opts, "test", &progress.NoopPrinter{}) rtest.OK(t, err) rtest.Equals(t, "", password, "got unexpected password") opts.password = "invalid" - _, err = ReadPassword(context.TODO(), opts, "test") + _, err = ReadPassword(context.TODO(), opts, "test", &progress.NoopPrinter{}) rtest.Assert(t, strings.Contains(err.Error(), "must not be specified together with providing a password via a cli option or environment variable"), "unexpected error message, got %v", err) } diff --git a/cmd/restic/lock.go b/cmd/restic/lock.go index f50d5633d..eb95c4432 100644 --- a/cmd/restic/lock.go +++ b/cmd/restic/lock.go @@ -8,7 +8,7 @@ import ( ) func internalOpenWithLocked(ctx context.Context, gopts GlobalOptions, dryRun bool, exclusive bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) { - repo, err := OpenRepository(ctx, gopts) + repo, err := OpenRepository(ctx, gopts, printer) if err != nil { return nil, nil, nil, err } diff --git a/cmd/restic/secondary_repo.go b/cmd/restic/secondary_repo.go index db4c93bad..16c75f1ab 100644 --- a/cmd/restic/secondary_repo.go +++ b/cmd/restic/secondary_repo.go @@ -5,6 +5,7 @@ import ( "os" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/ui/progress" "github.com/spf13/pflag" ) @@ -59,7 +60,7 @@ func (opts *secondaryRepoOptions) AddFlags(f *pflag.FlagSet, repoPrefix string, opts.PasswordCommand = os.Getenv("RESTIC_FROM_PASSWORD_COMMAND") } -func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gopts GlobalOptions, repoPrefix string) (GlobalOptions, bool, error) { +func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gopts GlobalOptions, repoPrefix string, printer progress.Printer) (GlobalOptions, bool, error) { if opts.Repo == "" && opts.RepositoryFile == "" && opts.LegacyRepo == "" && opts.LegacyRepositoryFile == "" { return GlobalOptions{}, false, errors.Fatal("Please specify a source repository location (--from-repo or --from-repository-file)") } @@ -115,7 +116,7 @@ func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gop return GlobalOptions{}, false, err } } - dstGopts.password, err = ReadPassword(ctx, dstGopts, "enter password for "+repoPrefix+" repository: ") + dstGopts.password, err = ReadPassword(ctx, dstGopts, "enter password for "+repoPrefix+" repository: ", printer) if err != nil { return GlobalOptions{}, false, err } diff --git a/cmd/restic/secondary_repo_test.go b/cmd/restic/secondary_repo_test.go index aa511ca99..2c31bcecf 100644 --- a/cmd/restic/secondary_repo_test.go +++ b/cmd/restic/secondary_repo_test.go @@ -7,6 +7,7 @@ import ( "testing" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui/progress" ) // TestFillSecondaryGlobalOpts tests valid and invalid data on fillSecondaryGlobalOpts-function @@ -171,7 +172,7 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { // Test all valid cases for _, testCase := range validSecondaryRepoTestCases { - DstGOpts, isFromRepo, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination") + DstGOpts, isFromRepo, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination", &progress.NoopPrinter{}) rtest.OK(t, err) rtest.Equals(t, DstGOpts, testCase.DstGOpts) rtest.Equals(t, isFromRepo, testCase.FromRepo) @@ -179,7 +180,7 @@ func TestFillSecondaryGlobalOpts(t *testing.T) { // Test all invalid cases for _, testCase := range invalidSecondaryRepoTestCases { - _, _, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination") + _, _, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination", &progress.NoopPrinter{}) rtest.Assert(t, err != nil, "Expected error, but function did not return an error") } }