diff --git a/cmd/restic/cmd_key_add.go b/cmd/restic/cmd_key_add.go index a7670a842..7e598abd2 100644 --- a/cmd/restic/cmd_key_add.go +++ b/cmd/restic/cmd_key_add.go @@ -6,6 +6,8 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/repository" + "github.com/restic/restic/internal/ui/progress" + "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -30,7 +32,9 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runKeyAdd(cmd.Context(), globalOptions, opts, args) + term, cancel := setupTermstatus() + defer cancel() + return runKeyAdd(cmd.Context(), globalOptions, opts, args, term) }, } @@ -52,21 +56,22 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) { flags.StringVarP(&opts.Hostname, "host", "", "", "the hostname for new key") } -func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string) error { +func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string, term *termstatus.Terminal) error { if len(args) > 0 { return fmt.Errorf("the key add command expects no arguments, only options - please see `restic help key add` for usage and flags") } + printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithAppendLock(ctx, gopts, false) if err != nil { return err } defer unlock() - return addKey(ctx, repo, gopts, opts) + return addKey(ctx, repo, gopts, opts, printer) } -func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyAddOptions) error { +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) if err != nil { return err @@ -82,7 +87,7 @@ func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOption return err } - Verbosef("saved new key with ID %s\n", id.ID()) + printer.P("saved new key with ID %s", id.ID()) return nil } diff --git a/cmd/restic/cmd_key_integration_test.go b/cmd/restic/cmd_key_integration_test.go index 8a6b2707f..95e5a912d 100644 --- a/cmd/restic/cmd_key_integration_test.go +++ b/cmd/restic/cmd_key_integration_test.go @@ -12,11 +12,14 @@ 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/termstatus" ) func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string { buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error { - return runKeyList(context.TODO(), gopts, []string{}) + return withTermStatus(gopts, func(ctx context.Context, term *termstatus.Terminal) error { + return runKeyList(ctx, gopts, []string{}, term) + }) }) rtest.OK(t, err) @@ -39,7 +42,9 @@ func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts GlobalOptions) testKeyNewPassword = "" }() - rtest.OK(t, runKeyAdd(context.TODO(), gopts, KeyAddOptions{}, []string{})) + term, cancel := setupTermstatus() + defer cancel() + rtest.OK(t, runKeyAdd(context.TODO(), gopts, KeyAddOptions{}, []string{}, term)) } func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) { @@ -49,10 +54,12 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) { }() t.Log("adding key for john@example.com") + term, cancel := setupTermstatus() + defer cancel() rtest.OK(t, runKeyAdd(context.TODO(), gopts, KeyAddOptions{ Username: "john", Hostname: "example.com", - }, []string{})) + }, []string{}, term)) repo, err := OpenRepository(context.TODO(), gopts) rtest.OK(t, err) @@ -69,13 +76,17 @@ func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) { testKeyNewPassword = "" }() - rtest.OK(t, runKeyPasswd(context.TODO(), gopts, KeyPasswdOptions{}, []string{})) + term, cancel := setupTermstatus() + defer cancel() + rtest.OK(t, runKeyPasswd(context.TODO(), gopts, KeyPasswdOptions{}, []string{}, term)) } func testRunKeyRemove(t testing.TB, gopts GlobalOptions, IDs []string) { t.Logf("remove %d keys: %q\n", len(IDs), IDs) + term, cancel := setupTermstatus() + defer cancel() for _, id := range IDs { - rtest.OK(t, runKeyRemove(context.TODO(), gopts, []string{id})) + rtest.OK(t, runKeyRemove(context.TODO(), gopts, []string{id}, term)) } } @@ -105,7 +116,9 @@ func TestKeyAddRemove(t *testing.T) { env.gopts.password = passwordList[len(passwordList)-1] t.Logf("testing access with last password %q\n", env.gopts.password) - rtest.OK(t, runKeyList(context.TODO(), env.gopts, []string{})) + term, cancel := setupTermstatus() + defer cancel() + rtest.OK(t, runKeyList(context.TODO(), env.gopts, []string{}, term)) testRunCheck(t, env.gopts) testRunKeyAddNewKeyUserHost(t, env.gopts) @@ -116,10 +129,12 @@ func TestKeyAddInvalid(t *testing.T) { defer cleanup() testRunInit(t, env.gopts) + term, cancel := setupTermstatus() + defer cancel() err := runKeyAdd(context.TODO(), env.gopts, KeyAddOptions{ NewPasswordFile: "some-file", InsecureNoPassword: true, - }, []string{}) + }, []string{}, term) rtest.Assert(t, strings.Contains(err.Error(), "only either"), "unexpected error message, got %q", err) pwfile := filepath.Join(t.TempDir(), "pwfile") @@ -127,7 +142,7 @@ func TestKeyAddInvalid(t *testing.T) { err = runKeyAdd(context.TODO(), env.gopts, KeyAddOptions{ NewPasswordFile: pwfile, - }, []string{}) + }, []string{}, term) rtest.Assert(t, strings.Contains(err.Error(), "an empty password is not allowed by default"), "unexpected error message, got %q", err) } @@ -138,9 +153,11 @@ func TestKeyAddEmpty(t *testing.T) { defer cleanup() testRunInit(t, env.gopts) + term, cancel := setupTermstatus() + defer cancel() rtest.OK(t, runKeyAdd(context.TODO(), env.gopts, KeyAddOptions{ InsecureNoPassword: true, - }, []string{})) + }, []string{}, term)) env.gopts.password = "" env.gopts.InsecureNoPassword = true @@ -170,16 +187,20 @@ func TestKeyProblems(t *testing.T) { testKeyNewPassword = "" }() - err := runKeyPasswd(context.TODO(), env.gopts, KeyPasswdOptions{}, []string{}) + term, cancel := setupTermstatus() + defer cancel() + err := runKeyPasswd(context.TODO(), env.gopts, KeyPasswdOptions{}, []string{}, term) t.Log(err) rtest.Assert(t, err != nil, "expected passwd change to fail") - err = runKeyAdd(context.TODO(), env.gopts, KeyAddOptions{}, []string{}) + err = runKeyAdd(context.TODO(), env.gopts, KeyAddOptions{}, []string{}, term) t.Log(err) rtest.Assert(t, err != nil, "expected key adding to fail") t.Logf("testing access with initial password %q\n", env.gopts.password) - rtest.OK(t, runKeyList(context.TODO(), env.gopts, []string{})) + term2, cancel2 := setupTermstatus() + defer cancel2() + rtest.OK(t, runKeyList(context.TODO(), env.gopts, []string{}, term2)) testRunCheck(t, env.gopts) } @@ -192,23 +213,27 @@ func TestKeyCommandInvalidArguments(t *testing.T) { return &emptySaveBackend{r}, nil } - err := runKeyAdd(context.TODO(), env.gopts, KeyAddOptions{}, []string{"johndoe"}) + term, cancel := setupTermstatus() + defer cancel() + err := runKeyAdd(context.TODO(), env.gopts, KeyAddOptions{}, []string{"johndoe"}, term) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key add: %v", err) - err = runKeyPasswd(context.TODO(), env.gopts, KeyPasswdOptions{}, []string{"johndoe"}) + err = runKeyPasswd(context.TODO(), env.gopts, KeyPasswdOptions{}, []string{"johndoe"}, term) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key passwd: %v", err) - err = runKeyList(context.TODO(), env.gopts, []string{"johndoe"}) + term3, cancel3 := setupTermstatus() + defer cancel3() + err = runKeyList(context.TODO(), env.gopts, []string{"johndoe"}, term3) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key list: %v", err) - err = runKeyRemove(context.TODO(), env.gopts, []string{}) + err = runKeyRemove(context.TODO(), env.gopts, []string{}, term) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err) - err = runKeyRemove(context.TODO(), env.gopts, []string{"john", "doe"}) + err = runKeyRemove(context.TODO(), env.gopts, []string{"john", "doe"}, term) t.Log(err) rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err) } diff --git a/cmd/restic/cmd_key_list.go b/cmd/restic/cmd_key_list.go index 6a0509f0f..8dd0bc132 100644 --- a/cmd/restic/cmd_key_list.go +++ b/cmd/restic/cmd_key_list.go @@ -8,7 +8,9 @@ import ( "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/table" + "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" ) @@ -32,27 +34,30 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runKeyList(cmd.Context(), globalOptions, args) + term, cancel := setupTermstatus() + defer cancel() + return runKeyList(cmd.Context(), globalOptions, args, term) }, } return cmd } -func runKeyList(ctx context.Context, gopts GlobalOptions, args []string) error { +func runKeyList(ctx context.Context, gopts GlobalOptions, args []string, term *termstatus.Terminal) error { if len(args) > 0 { return fmt.Errorf("the key list command expects no arguments, only options - please see `restic help key list` for usage and flags") } + printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock) if err != nil { return err } defer unlock() - return listKeys(ctx, repo, gopts) + return listKeys(ctx, repo, gopts, printer) } -func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions) error { +func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions, printer progress.Printer) error { type keyInfo struct { Current bool `json:"current"` ID string `json:"id"` @@ -68,7 +73,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions err := restic.ParallelList(ctx, s, restic.KeyFile, s.Connections(), func(ctx context.Context, id restic.ID, _ int64) error { k, err := repository.LoadKey(ctx, s, id) if err != nil { - Warnf("LoadKey() failed: %v\n", err) + printer.E("LoadKey() failed: %v", err) return nil } diff --git a/cmd/restic/cmd_key_passwd.go b/cmd/restic/cmd_key_passwd.go index 378325216..45f4fc59a 100644 --- a/cmd/restic/cmd_key_passwd.go +++ b/cmd/restic/cmd_key_passwd.go @@ -6,6 +6,8 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/repository" + "github.com/restic/restic/internal/ui/progress" + "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -31,7 +33,9 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runKeyPasswd(cmd.Context(), globalOptions, opts, args) + term, cancel := setupTermstatus() + defer cancel() + return runKeyPasswd(cmd.Context(), globalOptions, opts, args, term) }, } @@ -47,21 +51,22 @@ func (opts *KeyPasswdOptions) AddFlags(flags *pflag.FlagSet) { opts.KeyAddOptions.Add(flags) } -func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string) error { +func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string, term *termstatus.Terminal) error { if len(args) > 0 { return fmt.Errorf("the key passwd command expects no arguments, only options - please see `restic help key passwd` for usage and flags") } + printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false) if err != nil { return err } defer unlock() - return changePassword(ctx, repo, gopts, opts) + return changePassword(ctx, repo, gopts, opts, printer) } -func changePassword(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyPasswdOptions) error { +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) if err != nil { return err @@ -83,7 +88,7 @@ func changePassword(ctx context.Context, repo *repository.Repository, gopts Glob return err } - Verbosef("saved new key as %s\n", id) + printer.P("saved new key as %s", id) return nil } diff --git a/cmd/restic/cmd_key_remove.go b/cmd/restic/cmd_key_remove.go index f6713e4c2..b4f84d06f 100644 --- a/cmd/restic/cmd_key_remove.go +++ b/cmd/restic/cmd_key_remove.go @@ -7,6 +7,8 @@ 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" ) @@ -29,27 +31,30 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runKeyRemove(cmd.Context(), globalOptions, args) + term, cancel := setupTermstatus() + defer cancel() + return runKeyRemove(cmd.Context(), globalOptions, args, term) }, } return cmd } -func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string) error { +func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string, term *termstatus.Terminal) error { if len(args) != 1 { return fmt.Errorf("key remove expects one argument as the key id") } + printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false) if err != nil { return err } defer unlock() - return deleteKey(ctx, repo, args[0]) + return deleteKey(ctx, repo, args[0], gopts, printer) } -func deleteKey(ctx context.Context, repo *repository.Repository, idPrefix string) error { +func deleteKey(ctx context.Context, repo *repository.Repository, idPrefix string, gopts GlobalOptions, printer progress.Printer) error { id, err := restic.Find(ctx, repo, restic.KeyFile, idPrefix) if err != nil { return err @@ -64,6 +69,6 @@ func deleteKey(ctx context.Context, repo *repository.Repository, idPrefix string return err } - Verbosef("removed key %v\n", id) + printer.P("removed key %v", id) return nil }