mirror of
https://github.com/restic/restic.git
synced 2025-12-12 18:02:38 +00:00
deduplicate termstatus setup
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -10,27 +11,27 @@ import (
|
|||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createGlobalContext() context.Context {
|
func createGlobalContext(stderr io.Writer) context.Context {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
ch := make(chan os.Signal, 1)
|
ch := make(chan os.Signal, 1)
|
||||||
go cleanupHandler(ch, cancel)
|
go cleanupHandler(ch, cancel, stderr)
|
||||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanupHandler handles the SIGINT and SIGTERM signals.
|
// cleanupHandler handles the SIGINT and SIGTERM signals.
|
||||||
func cleanupHandler(c <-chan os.Signal, cancel context.CancelFunc) {
|
func cleanupHandler(c <-chan os.Signal, cancel context.CancelFunc, stderr io.Writer) {
|
||||||
s := <-c
|
s := <-c
|
||||||
debug.Log("signal %v received, cleaning up", s)
|
debug.Log("signal %v received, cleaning up", s)
|
||||||
// ignore error as there's no good way to handle it
|
// ignore error as there's no good way to handle it
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "\rsignal %v received, cleaning up \n", s)
|
_, _ = fmt.Fprintf(stderr, "\rsignal %v received, cleaning up \n", s)
|
||||||
|
|
||||||
if val, _ := os.LookupEnv("RESTIC_DEBUG_STACKTRACE_SIGINT"); val != "" {
|
if val, _ := os.LookupEnv("RESTIC_DEBUG_STACKTRACE_SIGINT"); val != "" {
|
||||||
_, _ = os.Stderr.WriteString("\n--- STACKTRACE START ---\n\n")
|
_, _ = stderr.Write([]byte("\n--- STACKTRACE START ---\n\n"))
|
||||||
_, _ = os.Stderr.WriteString(debug.DumpStacktrace())
|
_, _ = stderr.Write([]byte(debug.DumpStacktrace()))
|
||||||
_, _ = os.Stderr.WriteString("\n--- STACKTRACE END ---\n")
|
_, _ = stderr.Write([]byte("\n--- STACKTRACE END ---\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/ui/backup"
|
"github.com/restic/restic/internal/ui/backup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newBackupCommand() *cobra.Command {
|
func newBackupCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts BackupOptions
|
var opts BackupOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -63,9 +63,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runBackup(cmd.Context(), opts, *globalOptions, globalOptions.term, args)
|
||||||
defer cancel()
|
|
||||||
return runBackup(cmd.Context(), opts, globalOptions, term, args)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,10 @@ import (
|
|||||||
"github.com/restic/restic/internal/fs"
|
"github.com/restic/restic/internal/fs"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) error {
|
func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) error {
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
t.Logf("backing up %v in %v", target, dir)
|
t.Logf("backing up %v in %v", target, dir)
|
||||||
if dir != "" {
|
if dir != "" {
|
||||||
cleanup := rtest.Chdir(t, dir)
|
cleanup := rtest.Chdir(t, dir)
|
||||||
@@ -25,7 +24,7 @@ func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true}
|
opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true}
|
||||||
return runBackup(ctx, opts, gopts, term, target)
|
return runBackup(ctx, opts, gopts, gopts.term, target)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCacheCommand() *cobra.Command {
|
func newCacheCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts CacheOptions
|
var opts CacheOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -34,9 +34,7 @@ Exit status is 1 if there was any error.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(_ *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runCache(opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runCache(opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"}
|
var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"}
|
||||||
|
|
||||||
func newCatCommand() *cobra.Command {
|
func newCatCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]",
|
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]",
|
||||||
Short: "Print internal objects to stdout",
|
Short: "Print internal objects to stdout",
|
||||||
@@ -34,9 +34,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runCat(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runCat(cmd.Context(), globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
ValidArgs: catAllowedCmds,
|
ValidArgs: catAllowedCmds,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCheckCommand() *cobra.Command {
|
func newCheckCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts CheckOptions
|
var opts CheckOptions
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "check [flags]",
|
Use: "check [flags]",
|
||||||
@@ -46,14 +46,12 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
summary, err := runCheck(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
summary, err := runCheck(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
if globalOptions.JSON {
|
if globalOptions.JSON {
|
||||||
if err != nil && summary.NumErrors == 0 {
|
if err != nil && summary.NumErrors == 0 {
|
||||||
summary.NumErrors = 1
|
summary.NumErrors = 1
|
||||||
}
|
}
|
||||||
term.Print(ui.ToJSONString(summary))
|
globalOptions.term.Print(ui.ToJSONString(summary))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunCheck(t testing.TB, gopts GlobalOptions) {
|
func testRunCheck(t testing.TB, gopts GlobalOptions) {
|
||||||
@@ -25,12 +24,12 @@ func testRunCheckMustFail(t testing.TB, gopts GlobalOptions) {
|
|||||||
|
|
||||||
func testRunCheckOutput(gopts GlobalOptions, checkUnused bool) (string, error) {
|
func testRunCheckOutput(gopts GlobalOptions, checkUnused bool) (string, error) {
|
||||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
opts := CheckOptions{
|
opts := CheckOptions{
|
||||||
ReadData: true,
|
ReadData: true,
|
||||||
CheckUnused: checkUnused,
|
CheckUnused: checkUnused,
|
||||||
}
|
}
|
||||||
_, err := runCheck(context.TODO(), opts, gopts, nil, term)
|
_, err := runCheck(context.TODO(), opts, gopts, nil, gopts.term)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCopyCommand() *cobra.Command {
|
func newCopyCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts CopyOptions
|
var opts CopyOptions
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "copy [flags] [snapshotID ...]",
|
Use: "copy [flags] [snapshotID ...]",
|
||||||
@@ -48,9 +48,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runCopy(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runCopy(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) {
|
func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) {
|
||||||
@@ -23,8 +22,8 @@ func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runCopy(context.TODO(), copyOpts, gopts, nil, term)
|
return runCopy(context.TODO(), copyOpts, gopts, nil, gopts.term)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,25 +31,25 @@ import (
|
|||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerDebugCommand(cmd *cobra.Command) {
|
func registerDebugCommand(cmd *cobra.Command, globalOptions *GlobalOptions) {
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newDebugCommand(),
|
newDebugCommand(globalOptions),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDebugCommand() *cobra.Command {
|
func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "debug",
|
Use: "debug",
|
||||||
Short: "Debug commands",
|
Short: "Debug commands",
|
||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
}
|
}
|
||||||
cmd.AddCommand(newDebugDumpCommand())
|
cmd.AddCommand(newDebugDumpCommand(globalOptions))
|
||||||
cmd.AddCommand(newDebugExamineCommand())
|
cmd.AddCommand(newDebugExamineCommand(globalOptions))
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDebugDumpCommand() *cobra.Command {
|
func newDebugDumpCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "dump [indexes|snapshots|all|packs]",
|
Use: "dump [indexes|snapshots|all|packs]",
|
||||||
Short: "Dump data structures",
|
Short: "Dump data structures",
|
||||||
@@ -68,15 +68,13 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runDebugDump(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runDebugDump(cmd.Context(), globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDebugExamineCommand() *cobra.Command {
|
func newDebugExamineCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts DebugExamineOptions
|
var opts DebugExamineOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -84,9 +82,7 @@ func newDebugExamineCommand() *cobra.Command {
|
|||||||
Short: "Examine a pack file",
|
Short: "Examine a pack file",
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runDebugExamine(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runDebugExamine(cmd.Context(), globalOptions, opts, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ package main
|
|||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
func registerDebugCommand(_ *cobra.Command) {
|
func registerDebugCommand(_ *cobra.Command, _ *GlobalOptions) {
|
||||||
// No commands to register in non-debug mode
|
// No commands to register in non-debug mode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newDiffCommand() *cobra.Command {
|
func newDiffCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts DiffOptions
|
var opts DiffOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -52,9 +52,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runDiff(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runDiff(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunDiffOutput(gopts GlobalOptions, firstSnapshotID string, secondSnapshotID string) (string, error) {
|
func testRunDiffOutput(gopts GlobalOptions, firstSnapshotID string, secondSnapshotID string) (string, error) {
|
||||||
@@ -20,8 +19,8 @@ func testRunDiffOutput(gopts GlobalOptions, firstSnapshotID string, secondSnapsh
|
|||||||
opts := DiffOptions{
|
opts := DiffOptions{
|
||||||
ShowMetadata: false,
|
ShowMetadata: false,
|
||||||
}
|
}
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runDiff(ctx, opts, gopts, []string{firstSnapshotID, secondSnapshotID}, term)
|
return runDiff(ctx, opts, gopts, []string{firstSnapshotID, secondSnapshotID}, gopts.term)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return buf.String(), err
|
return buf.String(), err
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newDumpCommand() *cobra.Command {
|
func newDumpCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts DumpOptions
|
var opts DumpOptions
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "dump [flags] snapshotID file",
|
Use: "dump [flags] snapshotID file",
|
||||||
@@ -47,9 +47,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runDump(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runDump(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newFeaturesCommand() *cobra.Command {
|
func newFeaturesCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "features",
|
Use: "features",
|
||||||
Short: "Print list of feature flags",
|
Short: "Print list of feature flags",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/walker"
|
"github.com/restic/restic/internal/walker"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newFindCommand() *cobra.Command {
|
func newFindCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts FindOptions
|
var opts FindOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -51,9 +51,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runFind(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runFind(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts GlobalOptions, pattern string) []byte {
|
func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts GlobalOptions, pattern string) []byte {
|
||||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||||
gopts.JSON = wantJSON
|
gopts.JSON = wantJSON
|
||||||
|
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runFind(ctx, opts, gopts, []string{pattern}, term)
|
return runFind(ctx, opts, gopts, []string{pattern}, gopts.term)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newForgetCommand() *cobra.Command {
|
func newForgetCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts ForgetOptions
|
var opts ForgetOptions
|
||||||
var pruneOpts PruneOptions
|
var pruneOpts PruneOptions
|
||||||
|
|
||||||
@@ -49,9 +49,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runForget(cmd.Context(), opts, pruneOpts, *globalOptions, globalOptions.term, args)
|
||||||
defer cancel()
|
|
||||||
return runForget(cmd.Context(), opts, pruneOpts, globalOptions, term, args)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,14 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunForgetMayFail(gopts GlobalOptions, opts ForgetOptions, args ...string) error {
|
func testRunForgetMayFail(gopts GlobalOptions, opts ForgetOptions, args ...string) error {
|
||||||
pruneOpts := PruneOptions{
|
pruneOpts := PruneOptions{
|
||||||
MaxUnused: "5%",
|
MaxUnused: "5%",
|
||||||
}
|
}
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runForget(context.TODO(), opts, pruneOpts, gopts, term, args)
|
return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.term, args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newGenerateCommand() *cobra.Command {
|
func newGenerateCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts generateOptions
|
var opts generateOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -31,9 +31,7 @@ Exit status is 1 if there was any error.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(_ *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runGenerate(opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runGenerate(opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
opts.AddFlags(cmd.Flags())
|
opts.AddFlags(cmd.Flags())
|
||||||
@@ -118,7 +116,7 @@ func runGenerate(opts generateOptions, gopts GlobalOptions, args []string, term
|
|||||||
}
|
}
|
||||||
|
|
||||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
||||||
cmdRoot := newRootCommand()
|
cmdRoot := newRootCommand(&GlobalOptions{})
|
||||||
|
|
||||||
if opts.ManDir != "" {
|
if opts.ManDir != "" {
|
||||||
err := writeManpages(cmdRoot, opts.ManDir, printer)
|
err := writeManpages(cmdRoot, opts.ManDir, printer)
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunGenerate(gopts GlobalOptions, opts generateOptions) ([]byte, error) {
|
func testRunGenerate(gopts GlobalOptions, opts generateOptions) ([]byte, error) {
|
||||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runGenerate(opts, gopts, []string{}, term)
|
return runGenerate(opts, gopts, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return buf.Bytes(), err
|
return buf.Bytes(), err
|
||||||
@@ -31,14 +30,14 @@ func TestGenerateStdout(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
output, err := testRunGenerate(globalOptions, tc.opts)
|
output, err := testRunGenerate(GlobalOptions{}, tc.opts)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
rtest.Assert(t, strings.Contains(string(output), "# "+tc.name+" completion for restic"), "has no expected completion header")
|
rtest.Assert(t, strings.Contains(string(output), "# "+tc.name+" completion for restic"), "has no expected completion header")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Generate shell completions to stdout for two shells", func(t *testing.T) {
|
t.Run("Generate shell completions to stdout for two shells", func(t *testing.T) {
|
||||||
_, err := testRunGenerate(globalOptions, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"})
|
_, err := testRunGenerate(GlobalOptions{}, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"})
|
||||||
rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails")
|
rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newInitCommand() *cobra.Command {
|
func newInitCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts InitOptions
|
var opts InitOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -35,9 +35,7 @@ Exit status is 1 if there was any error.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runInit(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runInit(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
opts.AddFlags(cmd.Flags())
|
opts.AddFlags(cmd.Flags())
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/restic/restic/internal/repository"
|
"github.com/restic/restic/internal/repository"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,8 +17,8 @@ func testRunInit(t testing.TB, opts GlobalOptions) {
|
|||||||
restic.TestDisableCheckPolynomial(t)
|
restic.TestDisableCheckPolynomial(t)
|
||||||
restic.TestSetLockTimeout(t, 0)
|
restic.TestSetLockTimeout(t, 0)
|
||||||
|
|
||||||
err := withTermStatus(opts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(opts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runInit(ctx, InitOptions{}, opts, nil, term)
|
return runInit(ctx, InitOptions{}, opts, nil, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
t.Logf("repository initialized at %v", opts.Repo)
|
t.Logf("repository initialized at %v", opts.Repo)
|
||||||
@@ -44,14 +43,14 @@ func TestInitCopyChunkerParams(t *testing.T) {
|
|||||||
password: env2.gopts.password,
|
password: env2.gopts.password,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runInit(ctx, initOpts, env.gopts, nil, term)
|
return runInit(ctx, initOpts, gopts, nil, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.Assert(t, err != nil, "expected invalid init options to fail")
|
rtest.Assert(t, err != nil, "expected invalid init options to fail")
|
||||||
|
|
||||||
initOpts.CopyChunkerParameters = true
|
initOpts.CopyChunkerParameters = true
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runInit(ctx, initOpts, env.gopts, nil, term)
|
return runInit(ctx, initOpts, gopts, nil, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newKeyCommand() *cobra.Command {
|
func newKeyCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "key",
|
Use: "key",
|
||||||
Short: "Manage keys (passwords)",
|
Short: "Manage keys (passwords)",
|
||||||
@@ -17,10 +17,10 @@ per repository.
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newKeyAddCommand(),
|
newKeyAddCommand(globalOptions),
|
||||||
newKeyListCommand(),
|
newKeyListCommand(globalOptions),
|
||||||
newKeyPasswdCommand(),
|
newKeyPasswdCommand(globalOptions),
|
||||||
newKeyRemoveCommand(),
|
newKeyRemoveCommand(globalOptions),
|
||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newKeyAddCommand() *cobra.Command {
|
func newKeyAddCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts KeyAddOptions
|
var opts KeyAddOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -32,9 +32,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runKeyAdd(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runKeyAdd(cmd.Context(), globalOptions, opts, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,13 @@ import (
|
|||||||
"github.com/restic/restic/internal/backend"
|
"github.com/restic/restic/internal/backend"
|
||||||
"github.com/restic/restic/internal/repository"
|
"github.com/restic/restic/internal/repository"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string {
|
func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string {
|
||||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyList(ctx, gopts, []string{}, term)
|
return runKeyList(ctx, gopts, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
@@ -43,8 +42,8 @@ func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts GlobalOptions)
|
|||||||
testKeyNewPassword = ""
|
testKeyNewPassword = ""
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, term)
|
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
}
|
}
|
||||||
@@ -56,11 +55,11 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
t.Log("adding key for john@example.com")
|
t.Log("adding key for john@example.com")
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||||
Username: "john",
|
Username: "john",
|
||||||
Hostname: "example.com",
|
Hostname: "example.com",
|
||||||
}, []string{}, term)
|
}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|
||||||
@@ -79,8 +78,8 @@ func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) {
|
|||||||
testKeyNewPassword = ""
|
testKeyNewPassword = ""
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, term)
|
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
}
|
}
|
||||||
@@ -88,8 +87,8 @@ func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) {
|
|||||||
func testRunKeyRemove(t testing.TB, gopts GlobalOptions, IDs []string) {
|
func testRunKeyRemove(t testing.TB, gopts GlobalOptions, IDs []string) {
|
||||||
t.Logf("remove %d keys: %q\n", len(IDs), IDs)
|
t.Logf("remove %d keys: %q\n", len(IDs), IDs)
|
||||||
for _, id := range IDs {
|
for _, id := range IDs {
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyRemove(ctx, gopts, []string{id}, term)
|
return runKeyRemove(ctx, gopts, []string{id}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
}
|
}
|
||||||
@@ -121,8 +120,8 @@ func TestKeyAddRemove(t *testing.T) {
|
|||||||
|
|
||||||
env.gopts.password = passwordList[len(passwordList)-1]
|
env.gopts.password = passwordList[len(passwordList)-1]
|
||||||
t.Logf("testing access with last password %q\n", env.gopts.password)
|
t.Logf("testing access with last password %q\n", env.gopts.password)
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyList(ctx, env.gopts, []string{}, term)
|
return runKeyList(ctx, gopts, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
@@ -135,21 +134,21 @@ func TestKeyAddInvalid(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
testRunInit(t, env.gopts)
|
testRunInit(t, env.gopts)
|
||||||
|
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{
|
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||||
NewPasswordFile: "some-file",
|
NewPasswordFile: "some-file",
|
||||||
InsecureNoPassword: true,
|
InsecureNoPassword: true,
|
||||||
}, []string{}, term)
|
}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.Assert(t, strings.Contains(err.Error(), "only either"), "unexpected error message, got %q", err)
|
rtest.Assert(t, strings.Contains(err.Error(), "only either"), "unexpected error message, got %q", err)
|
||||||
|
|
||||||
pwfile := filepath.Join(t.TempDir(), "pwfile")
|
pwfile := filepath.Join(t.TempDir(), "pwfile")
|
||||||
rtest.OK(t, os.WriteFile(pwfile, []byte{}, 0o666))
|
rtest.OK(t, os.WriteFile(pwfile, []byte{}, 0o666))
|
||||||
|
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{
|
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||||
NewPasswordFile: pwfile,
|
NewPasswordFile: pwfile,
|
||||||
}, []string{}, term)
|
}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.Assert(t, strings.Contains(err.Error(), "an empty password is not allowed by default"), "unexpected error message, got %q", err)
|
rtest.Assert(t, strings.Contains(err.Error(), "an empty password is not allowed by default"), "unexpected error message, got %q", err)
|
||||||
}
|
}
|
||||||
@@ -161,10 +160,10 @@ func TestKeyAddEmpty(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
testRunInit(t, env.gopts)
|
testRunInit(t, env.gopts)
|
||||||
|
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{
|
return runKeyAdd(ctx, gopts, KeyAddOptions{
|
||||||
InsecureNoPassword: true,
|
InsecureNoPassword: true,
|
||||||
}, []string{}, term)
|
}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|
||||||
@@ -196,21 +195,21 @@ func TestKeyProblems(t *testing.T) {
|
|||||||
testKeyNewPassword = ""
|
testKeyNewPassword = ""
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyPasswd(ctx, env.gopts, KeyPasswdOptions{}, []string{}, term)
|
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
rtest.Assert(t, err != nil, "expected passwd change to fail")
|
rtest.Assert(t, err != nil, "expected passwd change to fail")
|
||||||
|
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{}, []string{}, term)
|
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
rtest.Assert(t, err != nil, "expected key adding to fail")
|
rtest.Assert(t, err != nil, "expected key adding to fail")
|
||||||
|
|
||||||
t.Logf("testing access with initial password %q\n", env.gopts.password)
|
t.Logf("testing access with initial password %q\n", env.gopts.password)
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyList(ctx, env.gopts, []string{}, term)
|
return runKeyList(ctx, gopts, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
@@ -225,32 +224,32 @@ func TestKeyCommandInvalidArguments(t *testing.T) {
|
|||||||
return &emptySaveBackend{r}, nil
|
return &emptySaveBackend{r}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyAdd(ctx, env.gopts, KeyAddOptions{}, []string{"johndoe"}, term)
|
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{"johndoe"}, gopts.term)
|
||||||
})
|
})
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key add: %v", err)
|
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key add: %v", err)
|
||||||
|
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyPasswd(ctx, env.gopts, KeyPasswdOptions{}, []string{"johndoe"}, term)
|
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{"johndoe"}, gopts.term)
|
||||||
})
|
})
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key passwd: %v", err)
|
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key passwd: %v", err)
|
||||||
|
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyList(ctx, env.gopts, []string{"johndoe"}, term)
|
return runKeyList(ctx, gopts, []string{"johndoe"}, gopts.term)
|
||||||
})
|
})
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key list: %v", err)
|
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key list: %v", err)
|
||||||
|
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyRemove(ctx, env.gopts, []string{}, term)
|
return runKeyRemove(ctx, gopts, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err)
|
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err)
|
||||||
|
|
||||||
err = withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err = withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runKeyRemove(ctx, env.gopts, []string{"john", "doe"}, term)
|
return runKeyRemove(ctx, gopts, []string{"john", "doe"}, gopts.term)
|
||||||
})
|
})
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err)
|
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newKeyListCommand() *cobra.Command {
|
func newKeyListCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "list",
|
Use: "list",
|
||||||
Short: "List keys (passwords)",
|
Short: "List keys (passwords)",
|
||||||
@@ -34,9 +34,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runKeyList(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runKeyList(cmd.Context(), globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newKeyPasswdCommand() *cobra.Command {
|
func newKeyPasswdCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts KeyPasswdOptions
|
var opts KeyPasswdOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -33,9 +33,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runKeyPasswd(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runKeyPasswd(cmd.Context(), globalOptions, opts, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newKeyRemoveCommand() *cobra.Command {
|
func newKeyRemoveCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "remove [ID]",
|
Use: "remove [ID]",
|
||||||
Short: "Remove key ID (password) from the repository.",
|
Short: "Remove key ID (password) from the repository.",
|
||||||
@@ -31,9 +31,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runKeyRemove(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runKeyRemove(cmd.Context(), globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newListCommand() *cobra.Command {
|
func newListCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"}
|
var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"}
|
||||||
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|")
|
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|")
|
||||||
|
|
||||||
@@ -34,9 +34,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runList(cmd.Context(), *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runList(cmd.Context(), globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
ValidArgs: listAllowedArgs,
|
ValidArgs: listAllowedArgs,
|
||||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||||
|
|||||||
@@ -8,13 +8,12 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunList(t testing.TB, opts GlobalOptions, tpe string) restic.IDs {
|
func testRunList(t testing.TB, opts GlobalOptions, tpe string) restic.IDs {
|
||||||
buf, err := withCaptureStdout(opts, func(opts GlobalOptions) error {
|
buf, err := withCaptureStdout(opts, func(opts GlobalOptions) error {
|
||||||
return withTermStatus(opts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(opts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runList(ctx, opts, []string{tpe}, term)
|
return runList(ctx, opts, []string{tpe}, gopts.term)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/walker"
|
"github.com/restic/restic/internal/walker"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newLsCommand() *cobra.Command {
|
func newLsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts LsOptions
|
var opts LsOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -60,9 +60,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runLs(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runLs(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
opts.AddFlags(cmd.Flags())
|
opts.AddFlags(cmd.Flags())
|
||||||
|
|||||||
@@ -10,14 +10,13 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args []string) []byte {
|
func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args []string) []byte {
|
||||||
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
|
||||||
gopts.Quiet = true
|
gopts.Quiet = true
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runLs(context.TODO(), opts, gopts, args, term)
|
return runLs(context.TODO(), opts, gopts, args, gopts.term)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMigrateCommand() *cobra.Command {
|
func newMigrateCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts MigrateOptions
|
var opts MigrateOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -35,9 +35,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runMigrate(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runMigrate(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ import (
|
|||||||
"github.com/anacrolix/fuse/fs"
|
"github.com/anacrolix/fuse/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerMountCommand(cmdRoot *cobra.Command) {
|
func registerMountCommand(cmdRoot *cobra.Command, globalOptions *GlobalOptions) {
|
||||||
cmdRoot.AddCommand(newMountCommand())
|
cmdRoot.AddCommand(newMountCommand(globalOptions))
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMountCommand() *cobra.Command {
|
func newMountCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts MountOptions
|
var opts MountOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -82,9 +82,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runMount(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runMount(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ package main
|
|||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
func registerMountCommand(_ *cobra.Command) {
|
func registerMountCommand(_ *cobra.Command, _ *GlobalOptions) {
|
||||||
// Mount command not supported on these platforms
|
// Mount command not supported on these platforms
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -62,8 +61,8 @@ func testRunMount(t testing.TB, gopts GlobalOptions, dir string, wg *sync.WaitGr
|
|||||||
opts := MountOptions{
|
opts := MountOptions{
|
||||||
TimeTemplate: time.RFC3339,
|
TimeTemplate: time.RFC3339,
|
||||||
}
|
}
|
||||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runMount(context.TODO(), opts, gopts, []string{dir}, term)
|
return runMount(context.TODO(), opts, gopts, []string{dir}, gopts.term)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,8 +127,8 @@ func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapsh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newOptionsCommand() *cobra.Command {
|
func newOptionsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "options",
|
Use: "options",
|
||||||
Short: "Print list of extended options",
|
Short: "Print list of extended options",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newPruneCommand() *cobra.Command {
|
func newPruneCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts PruneOptions
|
var opts PruneOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -40,9 +40,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runPrune(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runPrune(cmd.Context(), opts, globalOptions, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/restic/restic/internal/backend"
|
"github.com/restic/restic/internal/backend"
|
||||||
"github.com/restic/restic/internal/repository"
|
"github.com/restic/restic/internal/repository"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunPrune(t testing.TB, gopts GlobalOptions, opts PruneOptions) {
|
func testRunPrune(t testing.TB, gopts GlobalOptions, opts PruneOptions) {
|
||||||
@@ -29,8 +28,8 @@ func testRunPruneOutput(gopts GlobalOptions, opts PruneOptions) error {
|
|||||||
defer func() {
|
defer func() {
|
||||||
gopts.backendTestHook = oldHook
|
gopts.backendTestHook = oldHook
|
||||||
}()
|
}()
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runPrune(context.TODO(), opts, gopts, term)
|
return runPrune(context.TODO(), opts, gopts, gopts.term)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,8 +98,8 @@ func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) {
|
|||||||
pruneOpts := PruneOptions{
|
pruneOpts := PruneOptions{
|
||||||
MaxUnused: "5%",
|
MaxUnused: "5%",
|
||||||
}
|
}
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runForget(context.TODO(), opts, pruneOpts, gopts, term, args)
|
return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.term, args)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
@@ -122,8 +121,8 @@ func testPrune(t *testing.T, pruneOpts PruneOptions, checkOpts CheckOptions) {
|
|||||||
|
|
||||||
createPrunableRepo(t, env)
|
createPrunableRepo(t, env)
|
||||||
testRunPrune(t, env.gopts, pruneOpts)
|
testRunPrune(t, env.gopts, pruneOpts)
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
_, err := runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.term)
|
||||||
return err
|
return err
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -158,8 +157,8 @@ func TestPruneWithDamagedRepository(t *testing.T) {
|
|||||||
env.gopts.backendTestHook = oldHook
|
env.gopts.backendTestHook = oldHook
|
||||||
}()
|
}()
|
||||||
// prune should fail
|
// prune should fail
|
||||||
rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runPrune(context.TODO(), pruneDefaultOptions, env.gopts, term)
|
return runPrune(context.TODO(), pruneDefaultOptions, gopts, gopts.term)
|
||||||
}), "prune should have reported index not complete error")
|
}), "prune should have reported index not complete error")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,8 +230,8 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
|
|||||||
if checkOK {
|
if checkOK {
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
} else {
|
} else {
|
||||||
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
_, err := runCheck(context.TODO(), optionsCheck, env.gopts, nil, term)
|
_, err := runCheck(context.TODO(), optionsCheck, gopts, nil, gopts.term)
|
||||||
return err
|
return err
|
||||||
}) != nil,
|
}) != nil,
|
||||||
"check should have reported an error")
|
"check should have reported an error")
|
||||||
@@ -242,8 +241,8 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
|
|||||||
testRunPrune(t, env.gopts, optionsPrune)
|
testRunPrune(t, env.gopts, optionsPrune)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
} else {
|
} else {
|
||||||
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.Assert(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runPrune(context.TODO(), optionsPrune, env.gopts, term)
|
return runPrune(context.TODO(), optionsPrune, gopts, gopts.term)
|
||||||
}) != nil,
|
}) != nil,
|
||||||
"prune should have reported an error")
|
"prune should have reported an error")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRecoverCommand() *cobra.Command {
|
func newRecoverCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "recover [flags]",
|
Use: "recover [flags]",
|
||||||
Short: "Recover data from the repository not referenced by snapshots",
|
Short: "Recover data from the repository not referenced by snapshots",
|
||||||
@@ -35,9 +35,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runRecover(cmd.Context(), *globalOptions, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runRecover(cmd.Context(), globalOptions, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunRecover(t testing.TB, gopts GlobalOptions) {
|
func testRunRecover(t testing.TB, gopts GlobalOptions) {
|
||||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRecover(context.TODO(), gopts, term)
|
return runRecover(context.TODO(), gopts, gopts.term)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ func TestRecover(t *testing.T) {
|
|||||||
ids = testListSnapshots(t, env.gopts, 1)
|
ids = testListSnapshots(t, env.gopts, 1)
|
||||||
testRunCheck(t, env.gopts)
|
testRunCheck(t, env.gopts)
|
||||||
// check that the root tree is included in the snapshot
|
// check that the root tree is included in the snapshot
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runCat(context.TODO(), env.gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, term)
|
return runCat(context.TODO(), gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, gopts.term)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRepairCommand() *cobra.Command {
|
func newRepairCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "repair",
|
Use: "repair",
|
||||||
Short: "Repair the repository",
|
Short: "Repair the repository",
|
||||||
@@ -13,9 +13,9 @@ func newRepairCommand() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newRepairIndexCommand(),
|
newRepairIndexCommand(globalOptions),
|
||||||
newRepairPacksCommand(),
|
newRepairPacksCommand(globalOptions),
|
||||||
newRepairSnapshotsCommand(),
|
newRepairSnapshotsCommand(globalOptions),
|
||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRepairIndexCommand() *cobra.Command {
|
func newRepairIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts RepairIndexOptions
|
var opts RepairIndexOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -30,9 +30,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,10 +47,10 @@ func (opts *RepairIndexOptions) AddFlags(f *pflag.FlagSet) {
|
|||||||
f.BoolVar(&opts.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch")
|
f.BoolVar(&opts.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch")
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRebuildIndexCommand() *cobra.Command {
|
func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts RepairIndexOptions
|
var opts RepairIndexOptions
|
||||||
|
|
||||||
replacement := newRepairIndexCommand()
|
replacement := newRepairIndexCommand(globalOptions)
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "rebuild-index [flags]",
|
Use: "rebuild-index [flags]",
|
||||||
Short: replacement.Short,
|
Short: replacement.Short,
|
||||||
@@ -62,9 +60,7 @@ func newRebuildIndexCommand() *cobra.Command {
|
|||||||
// must create a new instance of the run function as it captures opts
|
// must create a new instance of the run function as it captures opts
|
||||||
// by reference
|
// by reference
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runRebuildIndex(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,15 +13,12 @@ import (
|
|||||||
"github.com/restic/restic/internal/repository/index"
|
"github.com/restic/restic/internal/repository/index"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) {
|
func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) {
|
||||||
rtest.OK(t, withRestoreGlobalOptions(func() error {
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
gopts.stdout = io.Discard
|
||||||
gopts.stdout = io.Discard
|
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term)
|
||||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, term)
|
|
||||||
})
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,14 +125,12 @@ func TestRebuildIndexFailsOnAppendOnly(t *testing.T) {
|
|||||||
datafile := filepath.Join("..", "..", "internal", "checker", "testdata", "duplicate-packs-in-index-test-repo.tar.gz")
|
datafile := filepath.Join("..", "..", "internal", "checker", "testdata", "duplicate-packs-in-index-test-repo.tar.gz")
|
||||||
rtest.SetupTarTestFixture(t, env.base, datafile)
|
rtest.SetupTarTestFixture(t, env.base, datafile)
|
||||||
|
|
||||||
err := withRestoreGlobalOptions(func() error {
|
env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) {
|
||||||
env.gopts.backendTestHook = func(r backend.Backend) (backend.Backend, error) {
|
return &appendOnlyBackend{r}, nil
|
||||||
return &appendOnlyBackend{r}, nil
|
}
|
||||||
}
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
gopts.stdout = io.Discard
|
||||||
env.gopts.stdout = io.Discard
|
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term)
|
||||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, env.gopts, term)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRepairPacksCommand() *cobra.Command {
|
func newRepairPacksCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "packs [packIDs...]",
|
Use: "packs [packIDs...]",
|
||||||
Short: "Salvage damaged pack files",
|
Short: "Salvage damaged pack files",
|
||||||
@@ -32,9 +32,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runRepairPacks(cmd.Context(), *globalOptions, globalOptions.term, args)
|
||||||
defer cancel()
|
|
||||||
return runRepairPacks(cmd.Context(), globalOptions, term, args)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRepairSnapshotsCommand() *cobra.Command {
|
func newRepairSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts RepairOptions
|
var opts RepairOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -50,9 +50,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runRepairSnapshots(cmd.Context(), *globalOptions, opts, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runRepairSnapshots(cmd.Context(), globalOptions, opts, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) {
|
func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) {
|
||||||
@@ -20,8 +19,8 @@ func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) {
|
|||||||
Forget: forget,
|
Forget: forget,
|
||||||
}
|
}
|
||||||
|
|
||||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRepairSnapshots(context.TODO(), gopts, opts, nil, term)
|
return runRepairSnapshots(context.TODO(), gopts, opts, nil, gopts.term)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRestoreCommand() *cobra.Command {
|
func newRestoreCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts RestoreOptions
|
var opts RestoreOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -46,9 +46,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runRestore(cmd.Context(), opts, *globalOptions, globalOptions.term, args)
|
||||||
defer cancel()
|
|
||||||
return runRestore(cmd.Context(), opts, globalOptions, term, args)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunRestore(t testing.TB, opts GlobalOptions, dir string, snapshotID string) {
|
func testRunRestore(t testing.TB, opts GlobalOptions, dir string, snapshotID string) {
|
||||||
@@ -31,8 +30,8 @@ func testRunRestoreExcludes(t testing.TB, gopts GlobalOptions, dir string, snaps
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testRunRestoreAssumeFailure(snapshotID string, opts RestoreOptions, gopts GlobalOptions) error {
|
func testRunRestoreAssumeFailure(snapshotID string, opts RestoreOptions, gopts GlobalOptions) error {
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRestore(ctx, opts, gopts, term, []string{snapshotID})
|
return runRestore(ctx, opts, gopts, gopts.term, []string{snapshotID})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,11 +336,8 @@ func TestRestoreWithPermissionFailure(t *testing.T) {
|
|||||||
|
|
||||||
snapshots := testListSnapshots(t, env.gopts, 1)
|
snapshots := testListSnapshots(t, env.gopts, 1)
|
||||||
|
|
||||||
_ = withRestoreGlobalOptions(func() error {
|
env.gopts.stderr = io.Discard
|
||||||
env.gopts.stderr = io.Discard
|
testRunRestore(t, env.gopts, filepath.Join(env.base, "restore"), snapshots[0].String())
|
||||||
testRunRestore(t, env.gopts, filepath.Join(env.base, "restore"), snapshots[0].String())
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// make sure that all files have been restored, regardless of any
|
// make sure that all files have been restored, regardless of any
|
||||||
// permission errors
|
// permission errors
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/walker"
|
"github.com/restic/restic/internal/walker"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRewriteCommand() *cobra.Command {
|
func newRewriteCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts RewriteOptions
|
var opts RewriteOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -60,9 +60,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runRewrite(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runRewrite(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ func testRunRewriteExclude(t testing.TB, gopts GlobalOptions, excludes []string,
|
|||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRewrite(context.TODO(), opts, gopts, nil, term)
|
return runRewrite(context.TODO(), opts, gopts, nil, gopts.term)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +41,9 @@ func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *rest
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var snapshots []*restic.Snapshot
|
var snapshots []*restic.Snapshot
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
ctx, repo, unlock, err := openWithReadLock(ctx, env.gopts, false, printer)
|
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@@ -117,9 +117,9 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) {
|
|||||||
testRunRewriteExclude(t, env.gopts, []string{}, true, metadata)
|
testRunRewriteExclude(t, env.gopts, []string{}, true, metadata)
|
||||||
|
|
||||||
var snapshots []*restic.Snapshot
|
var snapshots []*restic.Snapshot
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
ctx, repo, unlock, err := openWithReadLock(ctx, env.gopts, false, printer)
|
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@@ -157,17 +157,17 @@ func TestRewriteSnaphotSummary(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
createBasicRewriteRepo(t, env)
|
createBasicRewriteRepo(t, env)
|
||||||
|
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, env.gopts, []string{}, term)
|
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.term)
|
||||||
}))
|
}))
|
||||||
// no new snapshot should be created as the snapshot already has a summary
|
// no new snapshot should be created as the snapshot already has a summary
|
||||||
snapshots := testListSnapshots(t, env.gopts, 1)
|
snapshots := testListSnapshots(t, env.gopts, 1)
|
||||||
|
|
||||||
// replace snapshot by one without a summary
|
// replace snapshot by one without a summary
|
||||||
var oldSummary *restic.SnapshotSummary
|
var oldSummary *restic.SnapshotSummary
|
||||||
err := withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
_, repo, unlock, err := openWithExclusiveLock(ctx, env.gopts, false, printer)
|
_, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@@ -182,8 +182,8 @@ func TestRewriteSnaphotSummary(t *testing.T) {
|
|||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|
||||||
// rewrite snapshot and lookup ID of new snapshot
|
// rewrite snapshot and lookup ID of new snapshot
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, env.gopts, []string{}, term)
|
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.term)
|
||||||
}))
|
}))
|
||||||
newSnapshots := testListSnapshots(t, env.gopts, 2)
|
newSnapshots := testListSnapshots(t, env.gopts, 2)
|
||||||
newSnapshot := restic.NewIDSet(newSnapshots...).Sub(restic.NewIDSet(snapshots...)).List()[0]
|
newSnapshot := restic.NewIDSet(newSnapshots...).Sub(restic.NewIDSet(snapshots...)).List()[0]
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerSelfUpdateCommand(cmd *cobra.Command) {
|
func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *GlobalOptions) {
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newSelfUpdateCommand(),
|
newSelfUpdateCommand(globalOptions),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSelfUpdateCommand() *cobra.Command {
|
func newSelfUpdateCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts SelfUpdateOptions
|
var opts SelfUpdateOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -43,9 +43,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runSelfUpdate(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runSelfUpdate(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ package main
|
|||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
func registerSelfUpdateCommand(_ *cobra.Command) {
|
func registerSelfUpdateCommand(_ *cobra.Command, _ *GlobalOptions) {
|
||||||
// No commands to register in non-selfupdate mode
|
// No commands to register in non-selfupdate mode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newSnapshotsCommand() *cobra.Command {
|
func newSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts SnapshotOptions
|
var opts SnapshotOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -36,9 +36,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runSnapshots(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runSnapshots(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snapmap map[restic.ID]Snapshot) {
|
func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snapmap map[restic.ID]Snapshot) {
|
||||||
@@ -15,8 +14,8 @@ func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snap
|
|||||||
gopts.JSON = true
|
gopts.JSON = true
|
||||||
|
|
||||||
opts := SnapshotOptions{}
|
opts := SnapshotOptions{}
|
||||||
return withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
return withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runSnapshots(ctx, opts, gopts, []string{}, term)
|
return runSnapshots(ctx, opts, gopts, []string{}, gopts.term)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStatsCommand() *cobra.Command {
|
func newStatsCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts StatsOptions
|
var opts StatsOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -63,9 +63,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runStats(cmd.Context(), opts, *globalOptions, args, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runStats(cmd.Context(), opts, globalOptions, args, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/ui"
|
"github.com/restic/restic/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTagCommand() *cobra.Command {
|
func newTagCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts TagOptions
|
var opts TagOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -39,9 +39,7 @@ Exit status is 12 if the password is incorrect.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runTag(cmd.Context(), opts, *globalOptions, globalOptions.term, args)
|
||||||
defer cancel()
|
|
||||||
return runTag(cmd.Context(), opts, globalOptions, term, args)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
|
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
|
||||||
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runTag(context.TODO(), opts, gopts, term, []string{})
|
return runTag(context.TODO(), opts, gopts, gopts.term, []string{})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUnlockCommand() *cobra.Command {
|
func newUnlockCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
var opts UnlockOptions
|
var opts UnlockOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -27,9 +27,7 @@ Exit status is 1 if there was any error.
|
|||||||
GroupID: cmdGroupDefault,
|
GroupID: cmdGroupDefault,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
term, cancel := setupTermstatus()
|
return runUnlock(cmd.Context(), opts, *globalOptions, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
return runUnlock(cmd.Context(), opts, globalOptions, term)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
opts.AddFlags(cmd.Flags())
|
opts.AddFlags(cmd.Flags())
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newVersionCommand() *cobra.Command {
|
func newVersionCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "version",
|
Use: "version",
|
||||||
Short: "Print version information",
|
Short: "Print version information",
|
||||||
@@ -23,9 +23,7 @@ Exit status is 1 if there was any error.
|
|||||||
`,
|
`,
|
||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
Run: func(_ *cobra.Command, _ []string) {
|
Run: func(_ *cobra.Command, _ []string) {
|
||||||
term, cancel := setupTermstatus()
|
printer := newTerminalProgressPrinter(globalOptions.JSON, globalOptions.verbosity, globalOptions.term)
|
||||||
defer cancel()
|
|
||||||
printer := newTerminalProgressPrinter(globalOptions.JSON, globalOptions.verbosity, term)
|
|
||||||
|
|
||||||
if globalOptions.JSON {
|
if globalOptions.JSON {
|
||||||
type jsonVersion struct {
|
type jsonVersion struct {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
// TestFlags checks for double defined flags, the commands will panic on
|
// TestFlags checks for double defined flags, the commands will panic on
|
||||||
// ParseFlags() when a shorthand flag is defined twice.
|
// ParseFlags() when a shorthand flag is defined twice.
|
||||||
func TestFlags(t *testing.T) {
|
func TestFlags(t *testing.T) {
|
||||||
for _, cmd := range newRootCommand().Commands() {
|
for _, cmd := range newRootCommand(&GlobalOptions{}).Commands() {
|
||||||
t.Run(cmd.Name(), func(t *testing.T) {
|
t.Run(cmd.Name(), func(t *testing.T) {
|
||||||
cmd.Flags().SetOutput(io.Discard)
|
cmd.Flags().SetOutput(io.Discard)
|
||||||
err := cmd.ParseFlags([]string{"--help"})
|
err := cmd.ParseFlags([]string{"--help"})
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
"github.com/restic/restic/internal/terminal"
|
"github.com/restic/restic/internal/terminal"
|
||||||
"github.com/restic/restic/internal/textfile"
|
"github.com/restic/restic/internal/textfile"
|
||||||
|
"github.com/restic/restic/internal/ui"
|
||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
@@ -77,6 +78,7 @@ type GlobalOptions struct {
|
|||||||
password string
|
password string
|
||||||
stdout io.Writer
|
stdout io.Writer
|
||||||
stderr io.Writer
|
stderr io.Writer
|
||||||
|
term ui.Terminal
|
||||||
|
|
||||||
backends *location.Registry
|
backends *location.Registry
|
||||||
backendTestHook, backendInnerTestHook backendWrapper
|
backendTestHook, backendInnerTestHook backendWrapper
|
||||||
@@ -177,12 +179,6 @@ func (opts *GlobalOptions) PreRun(needsPassword bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalOptions = GlobalOptions{
|
|
||||||
stdout: os.Stdout,
|
|
||||||
stderr: os.Stderr,
|
|
||||||
backends: collectBackends(),
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectBackends() *location.Registry {
|
func collectBackends() *location.Registry {
|
||||||
backends := location.NewRegistry()
|
backends := location.NewRegistry()
|
||||||
backends.Register(azure.NewFactory())
|
backends.Register(azure.NewFactory())
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/restic/restic/internal/repository"
|
"github.com/restic/restic/internal/repository"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
"github.com/restic/restic/internal/ui/termstatus"
|
"github.com/restic/restic/internal/ui/termstatus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -222,12 +221,9 @@ func withTestEnvironment(t testing.TB) (env *testEnvironment, cleanup func()) {
|
|||||||
// replace this hook with "nil" if listing a filetype more than once is necessary
|
// replace this hook with "nil" if listing a filetype more than once is necessary
|
||||||
backendTestHook: func(r backend.Backend) (backend.Backend, error) { return newOrderedListOnceBackend(r), nil },
|
backendTestHook: func(r backend.Backend) (backend.Backend, error) { return newOrderedListOnceBackend(r), nil },
|
||||||
// start with default set of backends
|
// start with default set of backends
|
||||||
backends: globalOptions.backends,
|
backends: collectBackends(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// always overwrite global options
|
|
||||||
globalOptions = env.gopts
|
|
||||||
|
|
||||||
cleanup = func() {
|
cleanup = func() {
|
||||||
if !rtest.TestCleanupTempDirs {
|
if !rtest.TestCleanupTempDirs {
|
||||||
t.Logf("leaving temporary directory %v used for test", tempdir)
|
t.Logf("leaving temporary directory %v used for test", tempdir)
|
||||||
@@ -248,8 +244,8 @@ func testSetupBackupData(t testing.TB, env *testEnvironment) string {
|
|||||||
|
|
||||||
func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
||||||
var packs restic.IDSet
|
var packs restic.IDSet
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
@@ -267,8 +263,8 @@ func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
|||||||
|
|
||||||
func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
|
||||||
var treePacks restic.IDSet
|
var treePacks restic.IDSet
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
@@ -298,8 +294,8 @@ func captureBackend(gopts *GlobalOptions) func() backend.Backend {
|
|||||||
|
|
||||||
func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
|
func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
|
||||||
be := captureBackend(&gopts)
|
be := captureBackend(&gopts)
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
@@ -314,8 +310,8 @@ func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
|
|||||||
|
|
||||||
func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, removeTreePacks bool) {
|
func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, removeTreePacks bool) {
|
||||||
be := captureBackend(&gopts)
|
be := captureBackend(&gopts)
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
@@ -375,8 +371,8 @@ func lastSnapshot(old, new map[string]struct{}) (map[string]struct{}, string) {
|
|||||||
|
|
||||||
func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *restic.Snapshot {
|
func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *restic.Snapshot {
|
||||||
var snapshot *restic.Snapshot
|
var snapshot *restic.Snapshot
|
||||||
err := withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
err := withTermStatus(gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
@@ -420,30 +416,19 @@ func testFileSize(filename string, size int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func withRestoreGlobalOptions(inner func() error) error {
|
|
||||||
gopts := globalOptions
|
|
||||||
defer func() {
|
|
||||||
globalOptions = gopts
|
|
||||||
}()
|
|
||||||
return inner()
|
|
||||||
}
|
|
||||||
|
|
||||||
func withCaptureStdout(gopts GlobalOptions, inner func(gopts GlobalOptions) error) (*bytes.Buffer, error) {
|
func withCaptureStdout(gopts GlobalOptions, inner func(gopts GlobalOptions) error) (*bytes.Buffer, error) {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
err := withRestoreGlobalOptions(func() error {
|
gopts.stdout = buf
|
||||||
globalOptions.stdout = buf
|
err := inner(gopts)
|
||||||
gopts.stdout = buf
|
|
||||||
return inner(gopts)
|
|
||||||
})
|
|
||||||
|
|
||||||
return buf, err
|
return buf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func withTermStatus(gopts GlobalOptions, callback func(ctx context.Context, term ui.Terminal) error) error {
|
func withTermStatus(gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) error {
|
||||||
ctx, cancel := context.WithCancel(context.TODO())
|
ctx, cancel := context.WithCancel(context.TODO())
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
term := termstatus.New(gopts.stdout, gopts.stderr, gopts.Quiet)
|
term := termstatus.New(gopts.stdout, gopts.stderr, gopts.Quiet)
|
||||||
|
gopts.term = term
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
@@ -453,5 +438,5 @@ func withTermStatus(gopts GlobalOptions, callback func(ctx context.Context, term
|
|||||||
defer wg.Wait()
|
defer wg.Wait()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
return callback(ctx, term)
|
return callback(ctx, gopts)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
"github.com/restic/restic/internal/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCheckRestoreNoLock(t *testing.T) {
|
func TestCheckRestoreNoLock(t *testing.T) {
|
||||||
@@ -87,15 +86,15 @@ func TestListOnce(t *testing.T) {
|
|||||||
|
|
||||||
createPrunableRepo(t, env)
|
createPrunableRepo(t, env)
|
||||||
testRunPrune(t, env.gopts, pruneOpts)
|
testRunPrune(t, env.gopts, pruneOpts)
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
_, err := runCheck(context.TODO(), checkOpts, env.gopts, nil, term)
|
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.term)
|
||||||
return err
|
return err
|
||||||
}))
|
}))
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, env.gopts, term)
|
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.term)
|
||||||
}))
|
}))
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, env.gopts, term)
|
return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, gopts, gopts.term)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,9 +161,9 @@ func TestFindListOnce(t *testing.T) {
|
|||||||
thirdSnapshot := restic.NewIDSet(testListSnapshots(t, env.gopts, 3)...)
|
thirdSnapshot := restic.NewIDSet(testListSnapshots(t, env.gopts, 3)...)
|
||||||
|
|
||||||
var snapshotIDs restic.IDSet
|
var snapshotIDs restic.IDSet
|
||||||
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, term ui.Terminal) error {
|
rtest.OK(t, withTermStatus(env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
|
||||||
printer := newTerminalProgressPrinter(env.gopts.JSON, env.gopts.verbosity, term)
|
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, gopts.term)
|
||||||
ctx, repo, unlock, err := openWithReadLock(ctx, env.gopts, false, printer)
|
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
|
||||||
rtest.OK(t, err)
|
rtest.OK(t, err)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/feature"
|
"github.com/restic/restic/internal/feature"
|
||||||
"github.com/restic/restic/internal/repository"
|
"github.com/restic/restic/internal/repository"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
|
"github.com/restic/restic/internal/ui/termstatus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -31,7 +32,7 @@ var ErrOK = errors.New("ok")
|
|||||||
var cmdGroupDefault = "default"
|
var cmdGroupDefault = "default"
|
||||||
var cmdGroupAdvanced = "advanced"
|
var cmdGroupAdvanced = "advanced"
|
||||||
|
|
||||||
func newRootCommand() *cobra.Command {
|
func newRootCommand(globalOptions *GlobalOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "restic",
|
Use: "restic",
|
||||||
Short: "Backup and restore files",
|
Short: "Backup and restore files",
|
||||||
@@ -66,40 +67,41 @@ The full documentation can be found at https://restic.readthedocs.io/ .
|
|||||||
// Use our "generate" command instead of the cobra provided "completion" command
|
// Use our "generate" command instead of the cobra provided "completion" command
|
||||||
cmd.CompletionOptions.DisableDefaultCmd = true
|
cmd.CompletionOptions.DisableDefaultCmd = true
|
||||||
|
|
||||||
|
// globalOptions is passed to commands by reference to allow PersistentPreRunE to modify it
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
newBackupCommand(),
|
newBackupCommand(globalOptions),
|
||||||
newCacheCommand(),
|
newCacheCommand(globalOptions),
|
||||||
newCatCommand(),
|
newCatCommand(globalOptions),
|
||||||
newCheckCommand(),
|
newCheckCommand(globalOptions),
|
||||||
newCopyCommand(),
|
newCopyCommand(globalOptions),
|
||||||
newDiffCommand(),
|
newDiffCommand(globalOptions),
|
||||||
newDumpCommand(),
|
newDumpCommand(globalOptions),
|
||||||
newFeaturesCommand(),
|
newFeaturesCommand(globalOptions),
|
||||||
newFindCommand(),
|
newFindCommand(globalOptions),
|
||||||
newForgetCommand(),
|
newForgetCommand(globalOptions),
|
||||||
newGenerateCommand(),
|
newGenerateCommand(globalOptions),
|
||||||
newInitCommand(),
|
newInitCommand(globalOptions),
|
||||||
newKeyCommand(),
|
newKeyCommand(globalOptions),
|
||||||
newListCommand(),
|
newListCommand(globalOptions),
|
||||||
newLsCommand(),
|
newLsCommand(globalOptions),
|
||||||
newMigrateCommand(),
|
newMigrateCommand(globalOptions),
|
||||||
newOptionsCommand(),
|
newOptionsCommand(globalOptions),
|
||||||
newPruneCommand(),
|
newPruneCommand(globalOptions),
|
||||||
newRebuildIndexCommand(),
|
newRebuildIndexCommand(globalOptions),
|
||||||
newRecoverCommand(),
|
newRecoverCommand(globalOptions),
|
||||||
newRepairCommand(),
|
newRepairCommand(globalOptions),
|
||||||
newRestoreCommand(),
|
newRestoreCommand(globalOptions),
|
||||||
newRewriteCommand(),
|
newRewriteCommand(globalOptions),
|
||||||
newSnapshotsCommand(),
|
newSnapshotsCommand(globalOptions),
|
||||||
newStatsCommand(),
|
newStatsCommand(globalOptions),
|
||||||
newTagCommand(),
|
newTagCommand(globalOptions),
|
||||||
newUnlockCommand(),
|
newUnlockCommand(globalOptions),
|
||||||
newVersionCommand(),
|
newVersionCommand(globalOptions),
|
||||||
)
|
)
|
||||||
|
|
||||||
registerDebugCommand(cmd)
|
registerDebugCommand(cmd, globalOptions)
|
||||||
registerMountCommand(cmd)
|
registerMountCommand(cmd, globalOptions)
|
||||||
registerSelfUpdateCommand(cmd)
|
registerSelfUpdateCommand(cmd, globalOptions)
|
||||||
registerProfiling(cmd, os.Stderr)
|
registerProfiling(cmd, os.Stderr)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
@@ -125,7 +127,7 @@ func tweakGoGC() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printExitError(code int, message string) {
|
func printExitError(globalOptions GlobalOptions, code int, message string) {
|
||||||
if globalOptions.JSON {
|
if globalOptions.JSON {
|
||||||
type jsonExitError struct {
|
type jsonExitError struct {
|
||||||
MessageType string `json:"message_type"` // exit_error
|
MessageType string `json:"message_type"` // exit_error
|
||||||
@@ -139,7 +141,7 @@ func printExitError(code int, message string) {
|
|||||||
Message: message,
|
Message: message,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := json.NewEncoder(globalOptions.stderr).Encode(jsonS)
|
err := json.NewEncoder(os.Stderr).Encode(jsonS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// ignore error as there's no good way to handle it
|
// ignore error as there's no good way to handle it
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "JSON encode failed: %v\n", err)
|
_, _ = fmt.Fprintf(os.Stderr, "JSON encode failed: %v\n", err)
|
||||||
@@ -147,7 +149,7 @@ func printExitError(code int, message string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, _ = fmt.Fprintf(globalOptions.stderr, "%v\n", message)
|
_, _ = fmt.Fprintf(os.Stderr, "%v\n", message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,16 +172,26 @@ func main() {
|
|||||||
debug.Log("restic %s compiled with %v on %v/%v",
|
debug.Log("restic %s compiled with %v on %v/%v",
|
||||||
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||||
|
|
||||||
ctx := createGlobalContext()
|
globalOptions := GlobalOptions{
|
||||||
err = newRootCommand().ExecuteContext(ctx)
|
stdout: os.Stdout,
|
||||||
|
stderr: os.Stderr,
|
||||||
switch err {
|
backends: collectBackends(),
|
||||||
case nil:
|
|
||||||
err = ctx.Err()
|
|
||||||
case ErrOK:
|
|
||||||
// ErrOK overwrites context cancellation errors
|
|
||||||
err = nil
|
|
||||||
}
|
}
|
||||||
|
func() {
|
||||||
|
term, cancel := termstatus.Setup(os.Stdout, os.Stderr, globalOptions.Quiet)
|
||||||
|
defer cancel()
|
||||||
|
globalOptions.stdout, globalOptions.stderr = termstatus.WrapStdio(term)
|
||||||
|
globalOptions.term = term
|
||||||
|
ctx := createGlobalContext(os.Stderr)
|
||||||
|
err = newRootCommand(&globalOptions).ExecuteContext(ctx)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
err = ctx.Err()
|
||||||
|
case ErrOK:
|
||||||
|
// ErrOK overwrites context cancellation errors
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var exitMessage string
|
var exitMessage string
|
||||||
switch {
|
switch {
|
||||||
@@ -224,7 +236,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if exitCode != 0 {
|
if exitCode != 0 {
|
||||||
printExitError(exitCode, exitMessage)
|
printExitError(globalOptions, exitCode, exitMessage)
|
||||||
}
|
}
|
||||||
Exit(exitCode)
|
Exit(exitCode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/restic/restic/internal/ui/termstatus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// setupTermstatus creates a new termstatus and reroutes globalOptions.{stdout,stderr} to it
|
|
||||||
// The returned function must be called to shut down the termstatus,
|
|
||||||
//
|
|
||||||
// Expected usage:
|
|
||||||
// ```
|
|
||||||
// term, cancel := setupTermstatus()
|
|
||||||
// defer cancel()
|
|
||||||
// // do stuff
|
|
||||||
// ```
|
|
||||||
func setupTermstatus() (*termstatus.Terminal, func()) {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
// only shutdown once cancel is called to ensure that no output is lost
|
|
||||||
cancelCtx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
term := termstatus.New(globalOptions.stdout, globalOptions.stderr, globalOptions.Quiet)
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
term.Run(cancelCtx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// use the termstatus for stdout/stderr
|
|
||||||
prevStdout, prevStderr := globalOptions.stdout, globalOptions.stderr
|
|
||||||
globalOptions.stdout, globalOptions.stderr = termstatus.WrapStdio(term)
|
|
||||||
|
|
||||||
return term, func() {
|
|
||||||
// shutdown termstatus
|
|
||||||
globalOptions.stdout, globalOptions.stderr = prevStdout, prevStderr
|
|
||||||
cancel()
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/terminal"
|
"github.com/restic/restic/internal/terminal"
|
||||||
"github.com/restic/restic/internal/ui"
|
"github.com/restic/restic/internal/ui"
|
||||||
@@ -46,6 +47,34 @@ type fder interface {
|
|||||||
Fd() uintptr
|
Fd() uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup creates a new termstatus.
|
||||||
|
// The returned function must be called to shut down the termstatus,
|
||||||
|
//
|
||||||
|
// Expected usage:
|
||||||
|
// ```
|
||||||
|
// term, cancel := termstatus.Setup(os.stdout, os.stderr, false)
|
||||||
|
// defer cancel()
|
||||||
|
// // do stuff
|
||||||
|
// ```
|
||||||
|
func Setup(stdout, stderr io.Writer, quiet bool) (*Terminal, func()) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
// only shutdown once cancel is called to ensure that no output is lost
|
||||||
|
cancelCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
term := New(stdout, stderr, quiet)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
term.Run(cancelCtx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return term, func() {
|
||||||
|
// shutdown termstatus
|
||||||
|
cancel()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new Terminal for wr. A goroutine is started to update the
|
// New returns a new Terminal for wr. A goroutine is started to update the
|
||||||
// terminal. It is terminated when ctx is cancelled. When wr is redirected to
|
// terminal. It is terminated when ctx is cancelled. When wr is redirected to
|
||||||
// a file (e.g. via shell output redirection) or is just an io.Writer (not the
|
// a file (e.g. via shell output redirection) or is just an io.Writer (not the
|
||||||
|
|||||||
Reference in New Issue
Block a user