key: convert to termstatus

This commit is contained in:
Michael Eischer
2025-09-14 10:53:08 +02:00
parent fd8f8d64f5
commit 51299b8ea7
5 changed files with 82 additions and 37 deletions

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}