extract GlobalOptions into internal/global package

Rough steps:
```
mv cmd/restic/global* cmd/restic/secondary_repo* internal/global/
sed -i "s/package main/package global/" internal/global/*.go
Rename "GlobalOptions" to "Options" in internal/global/
Replace everywhere " GlobalOptions" -> " global.Options"
Replace everywhere "\*GlobalOptions" -> " *global.Options"
Make SecondaryRepoOptions public
Make create public
Make version public
```
This commit is contained in:
Michael Eischer
2025-10-12 17:56:28 +02:00
parent 2c677d8db4
commit a816b827cf
74 changed files with 407 additions and 333 deletions
+6 -5
View File
@@ -24,6 +24,7 @@ import (
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/textfile"
@@ -31,7 +32,7 @@ import (
"github.com/restic/restic/internal/ui/backup"
)
func newBackupCommand(globalOptions *GlobalOptions) *cobra.Command {
func newBackupCommand(globalOptions *global.Options) *cobra.Command {
var opts BackupOptions
cmd := &cobra.Command{
@@ -274,7 +275,7 @@ func readFilenamesRaw(r io.Reader) (names []string, err error) {
}
// Check returns an error when an invalid combination of options was set.
func (opts BackupOptions) Check(gopts GlobalOptions, args []string) error {
func (opts BackupOptions) Check(gopts global.Options, args []string) error {
if gopts.Password == "" && !gopts.InsecureNoPassword {
if opts.Stdin {
return errors.Fatal("cannot read both password and data from stdin")
@@ -475,7 +476,7 @@ func findParentSnapshot(ctx context.Context, repo restic.ListerLoaderUnpacked, o
return sn, err
}
func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, term ui.Terminal, args []string) error {
func runBackup(ctx context.Context, opts BackupOptions, gopts global.Options, term ui.Terminal, args []string) error {
var vsscfg fs.VSSConfig
var err error
@@ -509,7 +510,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
timeStamp := time.Now()
backupStart := timeStamp
if opts.TimeStamp != "" {
timeStamp, err = time.ParseInLocation(TimeFormat, opts.TimeStamp, time.Local)
timeStamp, err = time.ParseInLocation(global.TimeFormat, opts.TimeStamp, time.Local)
if err != nil {
return errors.Fatalf("error in time option: %v", err)
}
@@ -668,7 +669,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
Time: timeStamp,
Hostname: opts.Host,
ParentSnapshot: parentSnapshot,
ProgramVersion: "restic " + version,
ProgramVersion: "restic " + global.Version,
SkipIfUnchanged: opts.SkipIfUnchanged,
}
+5 -4
View File
@@ -12,12 +12,13 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) error {
return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts BackupOptions, gopts global.Options) error {
return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
t.Logf("backing up %v in %v", target, dir)
if dir != "" {
cleanup := rtest.Chdir(t, dir)
@@ -29,7 +30,7 @@ func testRunBackupAssumeFailure(t testing.TB, dir string, target []string, opts
})
}
func testRunBackup(t testing.TB, dir string, target []string, opts BackupOptions, gopts GlobalOptions) {
func testRunBackup(t testing.TB, dir string, target []string, opts BackupOptions, gopts global.Options) {
err := testRunBackupAssumeFailure(t, dir, target, opts, gopts)
rtest.Assert(t, err == nil, "Error while backing up: %v", err)
}
@@ -512,7 +513,7 @@ func TestBackupProgramVersion(t *testing.T) {
if newest == nil {
t.Fatal("expected a backup, got nil")
}
resticVersion := "restic " + version
resticVersion := "restic " + global.Version
rtest.Assert(t, newest.ProgramVersion == resticVersion,
"expected %v, got %v", resticVersion, newest.ProgramVersion)
}
+3 -2
View File
@@ -10,13 +10,14 @@ import (
"github.com/restic/restic/internal/backend/cache"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/table"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newCacheCommand(globalOptions *GlobalOptions) *cobra.Command {
func newCacheCommand(globalOptions *global.Options) *cobra.Command {
var opts CacheOptions
cmd := &cobra.Command{
@@ -55,7 +56,7 @@ func (opts *CacheOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.NoSize, "no-size", false, "do not output the size of the cache directories")
}
func runCache(opts CacheOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runCache(opts CacheOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if len(args) > 0 {
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -16,7 +17,7 @@ import (
var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"}
func newCatCommand(globalOptions *GlobalOptions) *cobra.Command {
func newCatCommand(globalOptions *global.Options) *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]",
Short: "Print internal objects to stdout",
@@ -65,7 +66,7 @@ func validateCatArgs(args []string) error {
return nil
}
func runCat(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runCat(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
if err := validateCatArgs(args); err != nil {
+4 -3
View File
@@ -16,13 +16,14 @@ import (
"github.com/restic/restic/internal/backend/cache"
"github.com/restic/restic/internal/checker"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress"
)
func newCheckCommand(globalOptions *GlobalOptions) *cobra.Command {
func newCheckCommand(globalOptions *global.Options) *cobra.Command {
var opts CheckOptions
cmd := &cobra.Command{
Use: "check [flags]",
@@ -170,7 +171,7 @@ func parsePercentage(s string) (float64, error) {
// - if the user explicitly requested --no-cache, we don't use any cache
// - if the user provides --cache-dir, we use a cache in a temporary sub-directory of the specified directory and the sub-directory is deleted after the check
// - by default, we use a cache in a temporary directory that is deleted after the check
func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress.Printer) (cleanup func()) {
func prepareCheckCache(opts CheckOptions, gopts *global.Options, printer progress.Printer) (cleanup func()) {
cleanup = func() {}
if opts.WithCache {
// use the default cache, no setup needed
@@ -217,7 +218,7 @@ func prepareCheckCache(opts CheckOptions, gopts *GlobalOptions, printer progress
return cleanup
}
func runCheck(ctx context.Context, opts CheckOptions, gopts GlobalOptions, args []string, term ui.Terminal) (checkSummary, error) {
func runCheck(ctx context.Context, opts CheckOptions, gopts global.Options, args []string, term ui.Terminal) (checkSummary, error) {
summary := checkSummary{MessageType: "summary"}
if len(args) != 0 {
return summary, errors.Fatal("the check command expects no arguments, only options - please see `restic help check` for usage and flags")
+5 -4
View File
@@ -4,10 +4,11 @@ import (
"context"
"testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunCheck(t testing.TB, gopts GlobalOptions) {
func testRunCheck(t testing.TB, gopts global.Options) {
t.Helper()
output, err := testRunCheckOutput(t, gopts, true)
if err != nil {
@@ -16,14 +17,14 @@ func testRunCheck(t testing.TB, gopts GlobalOptions) {
}
}
func testRunCheckMustFail(t testing.TB, gopts GlobalOptions) {
func testRunCheckMustFail(t testing.TB, gopts global.Options) {
t.Helper()
_, err := testRunCheckOutput(t, gopts, false)
rtest.Assert(t, err != nil, "expected non nil error after check of damaged repository")
}
func testRunCheckOutput(t testing.TB, gopts GlobalOptions, checkUnused bool) (string, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunCheckOutput(t testing.TB, gopts global.Options, checkUnused bool) (string, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
opts := CheckOptions{
ReadData: true,
CheckUnused: checkUnused,
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"testing"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/progress"
@@ -202,7 +203,7 @@ func TestPrepareCheckCache(t *testing.T) {
err := os.Remove(tmpDirBase)
rtest.OK(t, err)
}
gopts := GlobalOptions{CacheDir: tmpDirBase}
gopts := global.Options{CacheDir: tmpDirBase}
cleanup := prepareCheckCache(testCase.opts, &gopts, &progress.NoopPrinter{})
files, err := os.ReadDir(tmpDirBase)
rtest.OK(t, err)
@@ -232,7 +233,7 @@ func TestPrepareCheckCache(t *testing.T) {
}
func TestPrepareDefaultCheckCache(t *testing.T) {
gopts := GlobalOptions{CacheDir: ""}
gopts := global.Options{CacheDir: ""}
cleanup := prepareCheckCache(CheckOptions{}, &gopts, &progress.NoopPrinter{})
_, err := os.ReadDir(gopts.CacheDir)
rtest.OK(t, err)
+6 -5
View File
@@ -7,6 +7,7 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -17,7 +18,7 @@ import (
"github.com/spf13/pflag"
)
func newCopyCommand(globalOptions *GlobalOptions) *cobra.Command {
func newCopyCommand(globalOptions *global.Options) *cobra.Command {
var opts CopyOptions
cmd := &cobra.Command{
Use: "copy [flags] [snapshotID ...]",
@@ -60,18 +61,18 @@ Exit status is 12 if the password is incorrect.
// CopyOptions bundles all options for the copy command.
type CopyOptions struct {
secondaryRepoOptions
global.SecondaryRepoOptions
data.SnapshotFilter
}
func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) {
opts.secondaryRepoOptions.AddFlags(f, "destination", "to copy snapshots from")
opts.SecondaryRepoOptions.AddFlags(f, "destination", "to copy snapshots from")
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runCopy(ctx context.Context, opts CopyOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination")
secondaryGopts, isFromRepo, err := opts.SecondaryRepoOptions.FillGlobalOpts(ctx, gopts, "destination")
if err != nil {
return err
}
+5 -4
View File
@@ -6,23 +6,24 @@ import (
"path/filepath"
"testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunCopy(t testing.TB, srcGopts GlobalOptions, dstGopts GlobalOptions) {
func testRunCopy(t testing.TB, srcGopts global.Options, dstGopts global.Options) {
gopts := srcGopts
gopts.Repo = dstGopts.Repo
gopts.Password = dstGopts.Password
gopts.InsecureNoPassword = dstGopts.InsecureNoPassword
copyOpts := CopyOptions{
secondaryRepoOptions: secondaryRepoOptions{
SecondaryRepoOptions: global.SecondaryRepoOptions{
Repo: srcGopts.Repo,
password: srcGopts.Password,
Password: srcGopts.Password,
InsecureNoPassword: srcGopts.InsecureNoPassword,
},
}
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runCopy(context.TODO(), copyOpts, gopts, nil, gopts.Term)
}))
}
+7 -6
View File
@@ -24,6 +24,7 @@ import (
"github.com/restic/restic/internal/crypto"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/repository/index"
"github.com/restic/restic/internal/repository/pack"
@@ -32,13 +33,13 @@ import (
"github.com/restic/restic/internal/ui/progress"
)
func registerDebugCommand(cmd *cobra.Command, globalOptions *GlobalOptions) {
func registerDebugCommand(cmd *cobra.Command, globalOptions *global.Options) {
cmd.AddCommand(
newDebugCommand(globalOptions),
)
}
func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command {
func newDebugCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "debug",
Short: "Debug commands",
@@ -50,7 +51,7 @@ func newDebugCommand(globalOptions *GlobalOptions) *cobra.Command {
return cmd
}
func newDebugDumpCommand(globalOptions *GlobalOptions) *cobra.Command {
func newDebugDumpCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "dump [indexes|snapshots|all|packs]",
Short: "Dump data structures",
@@ -75,7 +76,7 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func newDebugExamineCommand(globalOptions *GlobalOptions) *cobra.Command {
func newDebugExamineCommand(globalOptions *global.Options) *cobra.Command {
var opts DebugExamineOptions
cmd := &cobra.Command{
@@ -184,7 +185,7 @@ func dumpIndexes(ctx context.Context, repo restic.ListerLoaderUnpacked, wr io.Wr
})
}
func runDebugDump(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runDebugDump(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if len(args) != 1 {
@@ -455,7 +456,7 @@ func storePlainBlob(id restic.ID, prefix string, plain []byte, printer progress.
return nil
}
func runDebugExamine(ctx context.Context, gopts GlobalOptions, opts DebugExamineOptions, args []string, term ui.Terminal) error {
func runDebugExamine(ctx context.Context, gopts global.Options, opts DebugExamineOptions, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if opts.ExtractPack && gopts.NoLock {
+5 -2
View File
@@ -2,8 +2,11 @@
package main
import "github.com/spf13/cobra"
import (
"github.com/restic/restic/internal/global"
"github.com/spf13/cobra"
)
func registerDebugCommand(_ *cobra.Command, _ *GlobalOptions) {
func registerDebugCommand(_ *cobra.Command, _ *global.Options) {
// No commands to register in non-debug mode
}
+3 -2
View File
@@ -10,13 +10,14 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newDiffCommand(globalOptions *GlobalOptions) *cobra.Command {
func newDiffCommand(globalOptions *global.Options) *cobra.Command {
var opts DiffOptions
cmd := &cobra.Command{
@@ -361,7 +362,7 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
return ctx.Err()
}
func runDiff(ctx context.Context, opts DiffOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runDiff(ctx context.Context, opts DiffOptions, gopts global.Options, args []string, term ui.Terminal) error {
if len(args) != 2 {
return errors.Fatalf("specify two snapshot IDs")
}
+3 -2
View File
@@ -11,11 +11,12 @@ import (
"strings"
"testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunDiffOutput(t testing.TB, gopts GlobalOptions, firstSnapshotID string, secondSnapshotID string) (string, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunDiffOutput(t testing.TB, gopts global.Options, firstSnapshotID string, secondSnapshotID string) (string, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
opts := DiffOptions{
ShowMetadata: false,
}
+3 -2
View File
@@ -11,6 +11,7 @@ import (
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/dump"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -18,7 +19,7 @@ import (
"github.com/spf13/pflag"
)
func newDumpCommand(globalOptions *GlobalOptions) *cobra.Command {
func newDumpCommand(globalOptions *global.Options) *cobra.Command {
var opts DumpOptions
cmd := &cobra.Command{
Use: "dump [flags] snapshotID file",
@@ -127,7 +128,7 @@ func printFromTree(ctx context.Context, tree *data.Tree, repo restic.BlobLoader,
return fmt.Errorf("path %q not found in snapshot", item)
}
func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runDump(ctx context.Context, opts DumpOptions, gopts global.Options, args []string, term ui.Terminal) error {
if len(args) != 2 {
return errors.Fatal("no file and no snapshot ID specified")
}
+2 -1
View File
@@ -3,12 +3,13 @@ package main
import (
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/feature"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui/table"
"github.com/spf13/cobra"
)
func newFeaturesCommand(globalOptions *GlobalOptions) *cobra.Command {
func newFeaturesCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "features",
Short: "Print list of feature flags",
+5 -4
View File
@@ -16,12 +16,13 @@ import (
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/walker"
)
func newFindCommand(globalOptions *GlobalOptions) *cobra.Command {
func newFindCommand(globalOptions *global.Options) *cobra.Command {
var opts FindOptions
cmd := &cobra.Command{
@@ -187,7 +188,7 @@ func (s *statefulOutput) PrintPatternNormal(path string, node *data.Node) {
s.printer.P("")
}
s.oldsn = s.newsn
s.printer.P("Found matching entries in snapshot %s from %s", s.oldsn.ID().Str(), s.oldsn.Time.Local().Format(TimeFormat))
s.printer.P("Found matching entries in snapshot %s from %s", s.oldsn.ID().Str(), s.oldsn.Time.Local().Format(global.TimeFormat))
}
s.printer.S(formatNode(path, node, s.ListLong, s.HumanReadable))
}
@@ -240,7 +241,7 @@ func (s *statefulOutput) PrintObjectNormal(kind, id, nodepath, treeID string, sn
} else {
s.printer.S(" ... path %s", nodepath)
}
s.printer.S(" ... in snapshot %s (%s)", sn.ID().Str(), sn.Time.Local().Format(TimeFormat))
s.printer.S(" ... in snapshot %s (%s)", sn.ID().Str(), sn.Time.Local().Format(global.TimeFormat))
}
func (s *statefulOutput) PrintObject(kind, id, nodepath, treeID string, sn *data.Snapshot) {
@@ -579,7 +580,7 @@ func (f *Finder) findObjectsPacks() {
}
}
func runFind(ctx context.Context, opts FindOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runFind(ctx context.Context, opts FindOptions, gopts global.Options, args []string, term ui.Terminal) error {
if len(args) == 0 {
return errors.Fatal("wrong number of arguments")
}
+3 -2
View File
@@ -7,11 +7,12 @@ import (
"testing"
"time"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts GlobalOptions, pattern string) []byte {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunFind(t testing.TB, wantJSON bool, opts FindOptions, gopts global.Options, pattern string) []byte {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.JSON = wantJSON
return runFind(ctx, opts, gopts, []string{pattern}, gopts.Term)
+3 -2
View File
@@ -9,13 +9,14 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newForgetCommand(globalOptions *GlobalOptions) *cobra.Command {
func newForgetCommand(globalOptions *global.Options) *cobra.Command {
var opts ForgetOptions
var pruneOpts PruneOptions
@@ -173,7 +174,7 @@ func verifyForgetOptions(opts *ForgetOptions) error {
return nil
}
func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOptions, gopts GlobalOptions, term ui.Terminal, args []string) error {
func runForget(ctx context.Context, opts ForgetOptions, pruneOptions PruneOptions, gopts global.Options, term ui.Terminal, args []string) error {
err := verifyForgetOptions(&opts)
if err != nil {
return err
+4 -3
View File
@@ -7,19 +7,20 @@ import (
"testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunForgetMayFail(t testing.TB, gopts GlobalOptions, opts ForgetOptions, args ...string) error {
func testRunForgetMayFail(t testing.TB, gopts global.Options, opts ForgetOptions, args ...string) error {
pruneOpts := PruneOptions{
MaxUnused: "5%",
}
return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runForget(context.TODO(), opts, pruneOpts, gopts, gopts.Term, args)
})
}
func testRunForget(t testing.TB, gopts GlobalOptions, opts ForgetOptions, args ...string) {
func testRunForget(t testing.TB, gopts global.Options, opts ForgetOptions, args ...string) {
rtest.OK(t, testRunForgetMayFail(t, gopts, opts, args...))
}
+5 -4
View File
@@ -6,6 +6,7 @@ import (
"time"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress"
"github.com/spf13/cobra"
@@ -13,7 +14,7 @@ import (
"github.com/spf13/pflag"
)
func newGenerateCommand(globalOptions *GlobalOptions) *cobra.Command {
func newGenerateCommand(globalOptions *global.Options) *cobra.Command {
var opts generateOptions
cmd := &cobra.Command{
@@ -72,7 +73,7 @@ func writeManpages(root *cobra.Command, dir string, printer progress.Printer) er
return doc.GenManTree(root, header, dir)
}
func writeCompletion(filename string, shell string, generate func(w io.Writer) error, printer progress.Printer, gopts GlobalOptions) (err error) {
func writeCompletion(filename string, shell string, generate func(w io.Writer) error, printer progress.Printer, gopts global.Options) (err error) {
printer.PT("writing %s completion file to %v", shell, filename)
var outWriter io.Writer
if filename != "-" {
@@ -110,13 +111,13 @@ func checkStdoutForSingleShell(opts generateOptions) error {
return nil
}
func runGenerate(opts generateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runGenerate(opts generateOptions, gopts global.Options, args []string, term ui.Terminal) error {
if len(args) > 0 {
return errors.Fatal("the generate command expects no arguments, only options - please see `restic help generate` for usage and flags")
}
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
cmdRoot := newRootCommand(&GlobalOptions{})
cmdRoot := newRootCommand(&global.Options{})
if opts.ManDir != "" {
err := writeManpages(cmdRoot, opts.ManDir, printer)
+5 -4
View File
@@ -5,11 +5,12 @@ import (
"strings"
"testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunGenerate(t testing.TB, gopts GlobalOptions, opts generateOptions) ([]byte, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunGenerate(t testing.TB, gopts global.Options, opts generateOptions) ([]byte, error) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runGenerate(opts, gopts, []string{}, gopts.Term)
})
return buf.Bytes(), err
@@ -28,14 +29,14 @@ func TestGenerateStdout(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
output, err := testRunGenerate(t, GlobalOptions{}, tc.opts)
output, err := testRunGenerate(t, global.Options{}, tc.opts)
rtest.OK(t, err)
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) {
_, err := testRunGenerate(t, GlobalOptions{}, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"})
_, err := testRunGenerate(t, global.Options{}, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"})
rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails")
})
}
+11 -10
View File
@@ -8,6 +8,7 @@ import (
"github.com/restic/chunker"
"github.com/restic/restic/internal/backend/location"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -17,7 +18,7 @@ import (
"github.com/spf13/pflag"
)
func newInitCommand(globalOptions *GlobalOptions) *cobra.Command {
func newInitCommand(globalOptions *global.Options) *cobra.Command {
var opts InitOptions
cmd := &cobra.Command{
@@ -44,18 +45,18 @@ Exit status is 1 if there was any error.
// InitOptions bundles all options for the init command.
type InitOptions struct {
secondaryRepoOptions
global.SecondaryRepoOptions
CopyChunkerParameters bool
RepositoryVersion string
}
func (opts *InitOptions) AddFlags(f *pflag.FlagSet) {
opts.secondaryRepoOptions.AddFlags(f, "secondary", "to copy chunker parameters from")
opts.SecondaryRepoOptions.AddFlags(f, "secondary", "to copy chunker parameters from")
f.BoolVar(&opts.CopyChunkerParameters, "copy-chunker-params", false, "copy chunker parameters from the secondary repository (useful with the copy command)")
f.StringVar(&opts.RepositoryVersion, "repository-version", "stable", "repository format version to use, allowed values are a format version, 'latest' and 'stable'")
}
func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runInit(ctx context.Context, opts InitOptions, gopts global.Options, args []string, term ui.Terminal) error {
if len(args) > 0 {
return errors.Fatal("the init command expects no arguments, only options - please see `restic help init` for usage and flags")
}
@@ -85,19 +86,19 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []
return err
}
gopts.Repo, err = ReadRepo(gopts)
gopts.Repo, err = global.ReadRepo(gopts)
if err != nil {
return err
}
gopts.Password, err = ReadPasswordTwice(ctx, gopts,
gopts.Password, err = global.ReadPasswordTwice(ctx, gopts,
"enter password for new repository: ",
"enter password again: ")
if err != nil {
return err
}
be, err := create(ctx, gopts.Repo, gopts, gopts.Extended, printer)
be, err := global.Create(ctx, gopts.Repo, gopts, gopts.Extended, printer)
if err != nil {
return errors.Fatalf("create repository at %s failed: %v", location.StripPassword(gopts.Backends, gopts.Repo), err)
}
@@ -137,14 +138,14 @@ func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []
return nil
}
func maybeReadChunkerPolynomial(ctx context.Context, opts InitOptions, gopts GlobalOptions, printer progress.Printer) (*chunker.Pol, error) {
func maybeReadChunkerPolynomial(ctx context.Context, opts InitOptions, gopts global.Options, printer progress.Printer) (*chunker.Pol, error) {
if opts.CopyChunkerParameters {
otherGopts, _, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "secondary")
otherGopts, _, err := opts.SecondaryRepoOptions.FillGlobalOpts(ctx, gopts, "secondary")
if err != nil {
return nil, err
}
otherRepo, err := OpenRepository(ctx, otherGopts, printer)
otherRepo, err := global.OpenRepository(ctx, otherGopts, printer)
if err != nil {
return nil, err
}
+11 -10
View File
@@ -6,18 +6,19 @@ import (
"path/filepath"
"testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/progress"
)
func testRunInit(t testing.TB, gopts GlobalOptions) {
func testRunInit(t testing.TB, gopts global.Options) {
repository.TestUseLowSecurityKDFParameters(t)
restic.TestDisableCheckPolynomial(t)
restic.TestSetLockTimeout(t, 0)
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runInit(ctx, InitOptions{}, gopts, nil, gopts.Term)
})
rtest.OK(t, err)
@@ -38,32 +39,32 @@ func TestInitCopyChunkerParams(t *testing.T) {
testRunInit(t, env2.gopts)
initOpts := InitOptions{
secondaryRepoOptions: secondaryRepoOptions{
SecondaryRepoOptions: global.SecondaryRepoOptions{
Repo: env2.gopts.Repo,
password: env2.gopts.Password,
Password: env2.gopts.Password,
},
}
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runInit(ctx, initOpts, gopts, nil, gopts.Term)
})
rtest.Assert(t, err != nil, "expected invalid init options to fail")
initOpts.CopyChunkerParameters = true
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runInit(ctx, initOpts, gopts, nil, gopts.Term)
})
rtest.OK(t, err)
var repo *repository.Repository
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
repo, err = OpenRepository(ctx, gopts, &progress.NoopPrinter{})
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
repo, err = global.OpenRepository(ctx, gopts, &progress.NoopPrinter{})
return err
})
rtest.OK(t, err)
var otherRepo *repository.Repository
err = withTermStatus(t, env2.gopts, func(ctx context.Context, gopts GlobalOptions) error {
otherRepo, err = OpenRepository(ctx, gopts, &progress.NoopPrinter{})
err = withTermStatus(t, env2.gopts, func(ctx context.Context, gopts global.Options) error {
otherRepo, err = global.OpenRepository(ctx, gopts, &progress.NoopPrinter{})
return err
})
rtest.OK(t, err)
+2 -1
View File
@@ -1,10 +1,11 @@
package main
import (
"github.com/restic/restic/internal/global"
"github.com/spf13/cobra"
)
func newKeyCommand(globalOptions *GlobalOptions) *cobra.Command {
func newKeyCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "key",
Short: "Manage keys (passwords)",
+7 -6
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress"
@@ -12,7 +13,7 @@ import (
"github.com/spf13/pflag"
)
func newKeyAddCommand(globalOptions *GlobalOptions) *cobra.Command {
func newKeyAddCommand(globalOptions *global.Options) *cobra.Command {
var opts KeyAddOptions
cmd := &cobra.Command{
@@ -54,7 +55,7 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) {
flags.StringVarP(&opts.Hostname, "host", "", "", "the hostname for new key")
}
func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string, term ui.Terminal) error {
func runKeyAdd(ctx context.Context, gopts global.Options, opts KeyAddOptions, args []string, term ui.Terminal) error {
if len(args) > 0 {
return fmt.Errorf("the key add command expects no arguments, only options - please see `restic help key add` for usage and flags")
}
@@ -69,7 +70,7 @@ func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, arg
return addKey(ctx, repo, gopts, opts, printer)
}
func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyAddOptions, printer progress.Printer) error {
func addKey(ctx context.Context, repo *repository.Repository, gopts global.Options, opts KeyAddOptions, printer progress.Printer) error {
pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword)
if err != nil {
return err
@@ -93,7 +94,7 @@ func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOption
// testKeyNewPassword is used to set a new password during integration testing.
var testKeyNewPassword string
func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile string, insecureNoPassword bool) (string, error) {
func getNewPassword(ctx context.Context, gopts global.Options, newPasswordFile string, insecureNoPassword bool) (string, error) {
if testKeyNewPassword != "" {
return testKeyNewPassword, nil
}
@@ -106,7 +107,7 @@ func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile st
}
if newPasswordFile != "" {
password, err := loadPasswordFromFile(newPasswordFile)
password, err := global.LoadPasswordFromFile(newPasswordFile)
if err != nil {
return "", err
}
@@ -123,7 +124,7 @@ func getNewPassword(ctx context.Context, gopts GlobalOptions, newPasswordFile st
// empty passwords are already handled above
newopts.InsecureNoPassword = false
return ReadPasswordTwice(ctx, newopts,
return global.ReadPasswordTwice(ctx, newopts,
"enter new password: ",
"enter password again: ")
}
+25 -24
View File
@@ -10,13 +10,14 @@ import (
"testing"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/progress"
)
func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunKeyListOtherIDs(t testing.TB, gopts global.Options) []string {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyList(ctx, gopts, []string{}, gopts.Term)
})
rtest.OK(t, err)
@@ -34,26 +35,26 @@ func testRunKeyListOtherIDs(t testing.TB, gopts GlobalOptions) []string {
return IDs
}
func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts GlobalOptions) {
func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts global.Options) {
testKeyNewPassword = newPassword
defer func() {
testKeyNewPassword = ""
}()
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.Term)
})
rtest.OK(t, err)
}
func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
func testRunKeyAddNewKeyUserHost(t testing.TB, gopts global.Options) {
testKeyNewPassword = "john's geheimnis"
defer func() {
testKeyNewPassword = ""
}()
t.Log("adding key for john@example.com")
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyAdd(ctx, gopts, KeyAddOptions{
Username: "john",
Hostname: "example.com",
@@ -61,8 +62,8 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
})
rtest.OK(t, err)
_ = withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
repo, err := OpenRepository(ctx, gopts, &progress.NoopPrinter{})
_ = withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
repo, err := global.OpenRepository(ctx, gopts, &progress.NoopPrinter{})
rtest.OK(t, err)
key, err := repository.SearchKey(ctx, repo, testKeyNewPassword, 2, "")
rtest.OK(t, err)
@@ -73,22 +74,22 @@ func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
})
}
func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) {
func testRunKeyPasswd(t testing.TB, newPassword string, gopts global.Options) {
testKeyNewPassword = newPassword
defer func() {
testKeyNewPassword = ""
}()
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term)
})
rtest.OK(t, err)
}
func testRunKeyRemove(t testing.TB, gopts GlobalOptions, IDs []string) {
func testRunKeyRemove(t testing.TB, gopts global.Options, IDs []string) {
t.Logf("remove %d keys: %q\n", len(IDs), IDs)
for _, id := range IDs {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyRemove(ctx, gopts, []string{id}, gopts.Term)
})
rtest.OK(t, err)
@@ -121,7 +122,7 @@ func TestKeyAddRemove(t *testing.T) {
env.gopts.Password = passwordList[len(passwordList)-1]
t.Logf("testing access with last password %q\n", env.gopts.Password)
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyList(ctx, gopts, []string{}, gopts.Term)
})
rtest.OK(t, err)
@@ -135,7 +136,7 @@ func TestKeyAddInvalid(t *testing.T) {
defer cleanup()
testRunInit(t, env.gopts)
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyAdd(ctx, gopts, KeyAddOptions{
NewPasswordFile: "some-file",
InsecureNoPassword: true,
@@ -146,7 +147,7 @@ func TestKeyAddInvalid(t *testing.T) {
pwfile := filepath.Join(t.TempDir(), "pwfile")
rtest.OK(t, os.WriteFile(pwfile, []byte{}, 0o666))
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyAdd(ctx, gopts, KeyAddOptions{
NewPasswordFile: pwfile,
}, []string{}, gopts.Term)
@@ -161,7 +162,7 @@ func TestKeyAddEmpty(t *testing.T) {
defer cleanup()
testRunInit(t, env.gopts)
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyAdd(ctx, gopts, KeyAddOptions{
InsecureNoPassword: true,
}, []string{}, gopts.Term)
@@ -196,20 +197,20 @@ func TestKeyProblems(t *testing.T) {
testKeyNewPassword = ""
}()
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{}, gopts.Term)
})
t.Log(err)
rtest.Assert(t, err != nil, "expected passwd change to fail")
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{}, gopts.Term)
})
t.Log(err)
rtest.Assert(t, err != nil, "expected key adding to fail")
t.Logf("testing access with initial password %q\n", env.gopts.Password)
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyList(ctx, gopts, []string{}, gopts.Term)
})
rtest.OK(t, err)
@@ -225,31 +226,31 @@ func TestKeyCommandInvalidArguments(t *testing.T) {
return &emptySaveBackend{r}, nil
}
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyAdd(ctx, gopts, KeyAddOptions{}, []string{"johndoe"}, gopts.Term)
})
t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key add: %v", err)
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyPasswd(ctx, gopts, KeyPasswdOptions{}, []string{"johndoe"}, gopts.Term)
})
t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key passwd: %v", err)
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyList(ctx, gopts, []string{"johndoe"}, gopts.Term)
})
t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "no arguments"), "unexpected error for key list: %v", err)
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyRemove(ctx, gopts, []string{}, gopts.Term)
})
t.Log(err)
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "one argument"), "unexpected error for key remove: %v", err)
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err = withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runKeyRemove(ctx, gopts, []string{"john", "doe"}, gopts.Term)
})
t.Log(err)
+5 -4
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"sync"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -14,7 +15,7 @@ import (
"github.com/spf13/cobra"
)
func newKeyListCommand(globalOptions *GlobalOptions) *cobra.Command {
func newKeyListCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List keys (passwords)",
@@ -40,7 +41,7 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func runKeyList(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runKeyList(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
if len(args) > 0 {
return fmt.Errorf("the key list command expects no arguments, only options - please see `restic help key list` for usage and flags")
}
@@ -55,7 +56,7 @@ func runKeyList(ctx context.Context, gopts GlobalOptions, args []string, term ui
return listKeys(ctx, repo, gopts, printer)
}
func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions, printer progress.Printer) error {
func listKeys(ctx context.Context, s *repository.Repository, gopts global.Options, printer progress.Printer) error {
type keyInfo struct {
Current bool `json:"current"`
ID string `json:"id"`
@@ -81,7 +82,7 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions
ShortID: id.Str(),
UserName: k.Username,
HostName: k.Hostname,
Created: k.Created.Local().Format(TimeFormat),
Created: k.Created.Local().Format(global.TimeFormat),
}
m.Lock()
+4 -3
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress"
@@ -12,7 +13,7 @@ import (
"github.com/spf13/pflag"
)
func newKeyPasswdCommand(globalOptions *GlobalOptions) *cobra.Command {
func newKeyPasswdCommand(globalOptions *global.Options) *cobra.Command {
var opts KeyPasswdOptions
cmd := &cobra.Command{
@@ -49,7 +50,7 @@ func (opts *KeyPasswdOptions) AddFlags(flags *pflag.FlagSet) {
opts.KeyAddOptions.Add(flags)
}
func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string, term ui.Terminal) error {
func runKeyPasswd(ctx context.Context, gopts global.Options, opts KeyPasswdOptions, args []string, term ui.Terminal) error {
if len(args) > 0 {
return fmt.Errorf("the key passwd command expects no arguments, only options - please see `restic help key passwd` for usage and flags")
}
@@ -64,7 +65,7 @@ func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOption
return changePassword(ctx, repo, gopts, opts, printer)
}
func changePassword(ctx context.Context, repo *repository.Repository, gopts GlobalOptions, opts KeyPasswdOptions, printer progress.Printer) error {
func changePassword(ctx context.Context, repo *repository.Repository, gopts global.Options, opts KeyPasswdOptions, printer progress.Printer) error {
pw, err := getNewPassword(ctx, gopts, opts.NewPasswordFile, opts.InsecureNoPassword)
if err != nil {
return err
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -12,7 +13,7 @@ import (
"github.com/spf13/cobra"
)
func newKeyRemoveCommand(globalOptions *GlobalOptions) *cobra.Command {
func newKeyRemoveCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "remove [ID]",
Short: "Remove key ID (password) from the repository.",
@@ -37,7 +38,7 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runKeyRemove(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
if len(args) != 1 {
return fmt.Errorf("key remove expects one argument as the key id")
}
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository/index"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -12,7 +13,7 @@ import (
"github.com/spf13/cobra"
)
func newListCommand(globalOptions *GlobalOptions) *cobra.Command {
func newListCommand(globalOptions *global.Options) *cobra.Command {
var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"}
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|")
@@ -42,7 +43,7 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func runList(ctx context.Context, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runList(ctx context.Context, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if len(args) != 1 {
+6 -5
View File
@@ -8,13 +8,14 @@ import (
"strings"
"testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui"
)
func testRunList(t testing.TB, gopts GlobalOptions, tpe string) restic.IDs {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunList(t testing.TB, gopts global.Options, tpe string) restic.IDs {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runList(ctx, gopts, []string{tpe}, gopts.Term)
})
rtest.OK(t, err)
@@ -50,7 +51,7 @@ func parseIDsFromReader(t testing.TB, rd io.Reader) restic.IDs {
return IDs
}
func testListSnapshots(t testing.TB, gopts GlobalOptions, expected int) restic.IDs {
func testListSnapshots(t testing.TB, gopts global.Options, expected int) restic.IDs {
t.Helper()
snapshotIDs := testRunList(t, gopts, "snapshots")
rtest.Assert(t, len(snapshotIDs) == expected, "expected %v snapshot, got %v", expected, snapshotIDs)
@@ -58,8 +59,8 @@ func testListSnapshots(t testing.TB, gopts GlobalOptions, expected int) restic.I
}
// extract blob set from repository index
func testListBlobs(t testing.TB, gopts GlobalOptions) (blobSetFromIndex restic.IDSet) {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testListBlobs(t testing.TB, gopts global.Options) (blobSetFromIndex restic.IDSet) {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err)
+3 -2
View File
@@ -18,12 +18,13 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/walker"
)
func newLsCommand(globalOptions *GlobalOptions) *cobra.Command {
func newLsCommand(globalOptions *global.Options) *cobra.Command {
var opts LsOptions
cmd := &cobra.Command{
@@ -303,7 +304,7 @@ type toSortOutput struct {
node *data.Node
}
func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runLs(ctx context.Context, opts LsOptions, gopts global.Options, args []string, term ui.Terminal) error {
termPrinter := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
if len(args) == 0 {
+4 -3
View File
@@ -9,12 +9,13 @@ import (
"testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args []string) []byte {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunLsWithOpts(t testing.TB, gopts global.Options, opts LsOptions, args []string) []byte {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.Quiet = true
return runLs(context.TODO(), opts, gopts, args, gopts.Term)
})
@@ -22,7 +23,7 @@ func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args [
return buf.Bytes()
}
func testRunLs(t testing.TB, gopts GlobalOptions, snapshotID string) []string {
func testRunLs(t testing.TB, gopts global.Options, snapshotID string) []string {
out := testRunLsWithOpts(t, gopts, LsOptions{}, []string{snapshotID})
return strings.Split(string(out), "\n")
}
+4 -3
View File
@@ -3,6 +3,7 @@ package main
import (
"context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/migrations"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -12,7 +13,7 @@ import (
"github.com/spf13/pflag"
)
func newMigrateCommand(globalOptions *GlobalOptions) *cobra.Command {
func newMigrateCommand(globalOptions *global.Options) *cobra.Command {
var opts MigrateOptions
cmd := &cobra.Command{
@@ -75,7 +76,7 @@ func checkMigrations(ctx context.Context, repo restic.Repository, printer progre
return nil
}
func applyMigrations(ctx context.Context, opts MigrateOptions, gopts GlobalOptions, repo restic.Repository, args []string, term ui.Terminal, printer progress.Printer) error {
func applyMigrations(ctx context.Context, opts MigrateOptions, gopts global.Options, repo restic.Repository, args []string, term ui.Terminal, printer progress.Printer) error {
var firsterr error
for _, name := range args {
found := false
@@ -133,7 +134,7 @@ func applyMigrations(ctx context.Context, opts MigrateOptions, gopts GlobalOptio
return firsterr
}
func runMigrate(ctx context.Context, opts MigrateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runMigrate(ctx context.Context, opts MigrateOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
+4 -3
View File
@@ -16,6 +16,7 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/fuse"
@@ -24,11 +25,11 @@ import (
"github.com/anacrolix/fuse/fs"
)
func registerMountCommand(cmdRoot *cobra.Command, globalOptions *GlobalOptions) {
func registerMountCommand(cmdRoot *cobra.Command, globalOptions *global.Options) {
cmdRoot.AddCommand(newMountCommand(globalOptions))
}
func newMountCommand(globalOptions *GlobalOptions) *cobra.Command {
func newMountCommand(globalOptions *global.Options) *cobra.Command {
var opts MountOptions
cmd := &cobra.Command{
@@ -114,7 +115,7 @@ func (opts *MountOptions) AddFlags(f *pflag.FlagSet) {
_ = f.MarkDeprecated("snapshot-template", "use --time-template")
}
func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runMount(ctx context.Context, opts MountOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
if opts.TimeTemplate == "" {
+5 -2
View File
@@ -3,8 +3,11 @@
package main
import "github.com/spf13/cobra"
import (
"github.com/restic/restic/internal/global"
"github.com/spf13/cobra"
)
func registerMountCommand(_ *cobra.Command, _ *GlobalOptions) {
func registerMountCommand(_ *cobra.Command, _ *global.Options) {
// Mount command not supported on these platforms
}
+5 -4
View File
@@ -15,6 +15,7 @@ import (
systemFuse "github.com/anacrolix/fuse"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui"
@@ -58,12 +59,12 @@ func waitForMount(t testing.TB, dir string) {
t.Errorf("subdir %q of dir %s never appeared", mountTestSubdir, dir)
}
func testRunMount(t testing.TB, gopts GlobalOptions, dir string, wg *sync.WaitGroup) {
func testRunMount(t testing.TB, gopts global.Options, dir string, wg *sync.WaitGroup) {
defer wg.Done()
opts := MountOptions{
TimeTemplate: time.RFC3339,
}
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runMount(context.TODO(), opts, gopts, []string{dir}, gopts.Term)
}))
}
@@ -91,7 +92,7 @@ func listSnapshots(t testing.TB, dir string) []string {
return names
}
func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapshotIDs restic.IDs, expectedSnapshotsInFuseDir int) {
func checkSnapshots(t testing.TB, gopts global.Options, mountpoint string, snapshotIDs restic.IDs, expectedSnapshotsInFuseDir int) {
t.Logf("checking for %d snapshots: %v", len(snapshotIDs), snapshotIDs)
var wg sync.WaitGroup
@@ -129,7 +130,7 @@ func checkSnapshots(t testing.TB, gopts GlobalOptions, mountpoint string, snapsh
}
}
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
if err != nil {
+2 -1
View File
@@ -3,12 +3,13 @@ package main
import (
"fmt"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/options"
"github.com/spf13/cobra"
)
func newOptionsCommand(globalOptions *GlobalOptions) *cobra.Command {
func newOptionsCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "options",
Short: "Print list of extended options",
+3 -2
View File
@@ -10,6 +10,7 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -19,7 +20,7 @@ import (
"github.com/spf13/pflag"
)
func newPruneCommand(globalOptions *GlobalOptions) *cobra.Command {
func newPruneCommand(globalOptions *global.Options) *cobra.Command {
var opts PruneOptions
cmd := &cobra.Command{
@@ -153,7 +154,7 @@ func verifyPruneOptions(opts *PruneOptions) error {
return nil
}
func runPrune(ctx context.Context, opts PruneOptions, gopts GlobalOptions, term ui.Terminal) error {
func runPrune(ctx context.Context, opts PruneOptions, gopts global.Options, term ui.Terminal) error {
err := verifyPruneOptions(&opts)
if err != nil {
return err
+11 -10
View File
@@ -7,28 +7,29 @@ import (
"testing"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
rtest "github.com/restic/restic/internal/test"
)
func testRunPrune(t testing.TB, gopts GlobalOptions, opts PruneOptions) {
func testRunPrune(t testing.TB, gopts global.Options, opts PruneOptions) {
t.Helper()
rtest.OK(t, testRunPruneOutput(t, gopts, opts))
}
func testRunPruneMustFail(t testing.TB, gopts GlobalOptions, opts PruneOptions) {
func testRunPruneMustFail(t testing.TB, gopts global.Options, opts PruneOptions) {
t.Helper()
err := testRunPruneOutput(t, gopts, opts)
rtest.Assert(t, err != nil, "expected non nil error")
}
func testRunPruneOutput(t testing.TB, gopts GlobalOptions, opts PruneOptions) error {
func testRunPruneOutput(t testing.TB, gopts global.Options, opts PruneOptions) error {
oldHook := gopts.BackendTestHook
gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) { return newListOnceBackend(r), nil }
defer func() {
gopts.BackendTestHook = oldHook
}()
return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runPrune(context.TODO(), opts, gopts, gopts.Term)
})
}
@@ -88,8 +89,8 @@ func createPrunableRepo(t *testing.T, env *testEnvironment) {
testRunForget(t, env.gopts, ForgetOptions{}, firstSnapshot.String())
}
func testRunForgetJSON(t testing.TB, gopts GlobalOptions, args ...string) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunForgetJSON(t testing.TB, gopts global.Options, args ...string) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.JSON = true
opts := ForgetOptions{
DryRun: true,
@@ -119,7 +120,7 @@ func testPrune(t *testing.T, pruneOpts PruneOptions, checkOpts CheckOptions) {
createPrunableRepo(t, env)
testRunPrune(t, env.gopts, pruneOpts)
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term)
return err
}))
@@ -155,7 +156,7 @@ func TestPruneWithDamagedRepository(t *testing.T) {
env.gopts.BackendTestHook = oldHook
}()
// prune should fail
rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.Equals(t, repository.ErrPacksMissing, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runPrune(context.TODO(), pruneDefaultOptions, gopts, gopts.Term)
}), "prune should have reported index not complete error")
}
@@ -228,7 +229,7 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
if checkOK {
testRunCheck(t, env.gopts)
} else {
rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
_, err := runCheck(context.TODO(), optionsCheck, gopts, nil, gopts.Term)
return err
}) != nil,
@@ -239,7 +240,7 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o
testRunPrune(t, env.gopts, optionsPrune)
testRunCheck(t, env.gopts)
} else {
rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.Assert(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runPrune(context.TODO(), optionsPrune, gopts, gopts.Term)
}) != nil,
"prune should have reported an error")
+3 -2
View File
@@ -7,6 +7,7 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -15,7 +16,7 @@ import (
"golang.org/x/sync/errgroup"
)
func newRecoverCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRecoverCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "recover [flags]",
Short: "Recover data from the repository not referenced by snapshots",
@@ -42,7 +43,7 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func runRecover(ctx context.Context, gopts GlobalOptions, term ui.Terminal) error {
func runRecover(ctx context.Context, gopts global.Options, term ui.Terminal) error {
hostname, err := os.Hostname()
if err != nil {
return err
+4 -3
View File
@@ -4,11 +4,12 @@ import (
"context"
"testing"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunRecover(t testing.TB, gopts GlobalOptions) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunRecover(t testing.TB, gopts global.Options) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRecover(context.TODO(), gopts, gopts.Term)
}))
}
@@ -32,7 +33,7 @@ func TestRecover(t *testing.T) {
ids = testListSnapshots(t, env.gopts, 1)
testRunCheck(t, env.gopts)
// check that the root tree is included in the snapshot
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runCat(context.TODO(), gopts, []string{"tree", ids[0].String() + ":" + sn.Tree.Str()}, gopts.Term)
}))
}
+2 -1
View File
@@ -1,10 +1,11 @@
package main
import (
"github.com/restic/restic/internal/global"
"github.com/spf13/cobra"
)
func newRepairCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRepairCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "repair",
Short: "Repair the repository",
+4 -3
View File
@@ -3,13 +3,14 @@ package main
import (
"context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newRepairIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRepairIndexCommand(globalOptions *global.Options) *cobra.Command {
var opts RepairIndexOptions
cmd := &cobra.Command{
@@ -47,7 +48,7 @@ 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")
}
func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRebuildIndexCommand(globalOptions *global.Options) *cobra.Command {
var opts RepairIndexOptions
replacement := newRepairIndexCommand(globalOptions)
@@ -68,7 +69,7 @@ func newRebuildIndexCommand(globalOptions *GlobalOptions) *cobra.Command {
return cmd
}
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term ui.Terminal) error {
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts global.Options, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
@@ -10,19 +10,20 @@ import (
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository/index"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
func testRunRebuildIndex(t testing.TB, gopts GlobalOptions) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunRebuildIndex(t testing.TB, gopts global.Options) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.Quiet = true
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term)
}))
}
func testRebuildIndex(t *testing.T, backendTestHook BackendWrapper) {
func testRebuildIndex(t *testing.T, backendTestHook global.BackendWrapper) {
env, cleanup := withTestEnvironment(t)
defer cleanup()
@@ -128,7 +129,7 @@ func TestRebuildIndexFailsOnAppendOnly(t *testing.T) {
env.gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) {
return &appendOnlyBackend{r}, nil
}
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
gopts.Quiet = true
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term)
})
+3 -2
View File
@@ -7,13 +7,14 @@ import (
"os"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
)
func newRepairPacksCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRepairPacksCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "packs [packIDs...]",
Short: "Salvage damaged pack files",
@@ -38,7 +39,7 @@ Exit status is 12 if the password is incorrect.
return cmd
}
func runRepairPacks(ctx context.Context, gopts GlobalOptions, term ui.Terminal, args []string) error {
func runRepairPacks(ctx context.Context, gopts global.Options, term ui.Terminal, args []string) error {
ids := restic.NewIDSet()
for _, arg := range args {
id, err := restic.ParseID(arg)
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/walker"
@@ -13,7 +14,7 @@ import (
"github.com/spf13/pflag"
)
func newRepairSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRepairSnapshotsCommand(globalOptions *global.Options) *cobra.Command {
var opts RepairOptions
cmd := &cobra.Command{
@@ -75,7 +76,7 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string, term ui.Terminal) error {
func runRepairSnapshots(ctx context.Context, gopts global.Options, opts RepairOptions, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun, printer)
@@ -10,16 +10,17 @@ import (
"reflect"
"testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) {
func testRunRepairSnapshot(t testing.TB, gopts global.Options, forget bool) {
opts := RepairOptions{
Forget: forget,
}
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRepairSnapshots(context.TODO(), gopts, opts, nil, gopts.Term)
}))
}
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restorer"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress"
@@ -18,7 +19,7 @@ import (
"github.com/spf13/pflag"
)
func newRestoreCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRestoreCommand(globalOptions *global.Options) *cobra.Command {
var opts RestoreOptions
cmd := &cobra.Command{
@@ -87,7 +88,7 @@ func (opts *RestoreOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.Delete, "delete", false, "delete files from target directory if they do not exist in snapshot. Use '--dry-run -vv' to check what would be deleted")
}
func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
func runRestore(ctx context.Context, opts RestoreOptions, gopts global.Options,
term ui.Terminal, args []string) error {
var printer restoreui.ProgressPrinter
+9 -8
View File
@@ -12,15 +12,16 @@ import (
"time"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
func testRunRestore(t testing.TB, gopts GlobalOptions, dir string, snapshotID string) {
func testRunRestore(t testing.TB, gopts global.Options, dir string, snapshotID string) {
testRunRestoreExcludes(t, gopts, dir, snapshotID, nil)
}
func testRunRestoreExcludes(t testing.TB, gopts GlobalOptions, dir string, snapshotID string, excludes []string) {
func testRunRestoreExcludes(t testing.TB, gopts global.Options, dir string, snapshotID string, excludes []string) {
opts := RestoreOptions{
Target: dir,
}
@@ -29,13 +30,13 @@ func testRunRestoreExcludes(t testing.TB, gopts GlobalOptions, dir string, snaps
rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID, opts, gopts))
}
func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOptions, gopts GlobalOptions) error {
return withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOptions, gopts global.Options) error {
return withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRestore(ctx, opts, gopts, gopts.Term, []string{snapshotID})
})
}
func testRunRestoreLatest(t testing.TB, gopts GlobalOptions, dir string, paths []string, hosts []string) {
func testRunRestoreLatest(t testing.TB, gopts global.Options, dir string, paths []string, hosts []string) {
opts := RestoreOptions{
Target: dir,
SnapshotFilter: data.SnapshotFilter{
@@ -47,7 +48,7 @@ func testRunRestoreLatest(t testing.TB, gopts GlobalOptions, dir string, paths [
rtest.OK(t, testRunRestoreAssumeFailure(t, "latest", opts, gopts))
}
func testRunRestoreIncludes(t testing.TB, gopts GlobalOptions, dir string, snapshotID restic.ID, includes []string) {
func testRunRestoreIncludes(t testing.TB, gopts global.Options, dir string, snapshotID restic.ID, includes []string) {
opts := RestoreOptions{
Target: dir,
}
@@ -56,7 +57,7 @@ func testRunRestoreIncludes(t testing.TB, gopts GlobalOptions, dir string, snaps
rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID.String(), opts, gopts))
}
func testRunRestoreIncludesFromFile(t testing.TB, gopts GlobalOptions, dir string, snapshotID restic.ID, includesFile string) {
func testRunRestoreIncludesFromFile(t testing.TB, gopts global.Options, dir string, snapshotID restic.ID, includesFile string) {
opts := RestoreOptions{
Target: dir,
}
@@ -65,7 +66,7 @@ func testRunRestoreIncludesFromFile(t testing.TB, gopts GlobalOptions, dir strin
rtest.OK(t, testRunRestoreAssumeFailure(t, snapshotID.String(), opts, gopts))
}
func testRunRestoreExcludesFromFile(t testing.TB, gopts GlobalOptions, dir string, snapshotID restic.ID, excludesFile string) {
func testRunRestoreExcludesFromFile(t testing.TB, gopts global.Options, dir string, snapshotID restic.ID, excludesFile string) {
opts := RestoreOptions{
Target: dir,
}
+4 -3
View File
@@ -12,6 +12,7 @@ import (
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
@@ -19,7 +20,7 @@ import (
"github.com/restic/restic/internal/walker"
)
func newRewriteCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRewriteCommand(globalOptions *global.Options) *cobra.Command {
var opts RewriteOptions
cmd := &cobra.Command{
@@ -91,7 +92,7 @@ func (sma snapshotMetadataArgs) convert() (*snapshotMetadata, error) {
var timeStamp *time.Time
if sma.Time != "" {
t, err := time.ParseInLocation(TimeFormat, sma.Time, time.Local)
t, err := time.ParseInLocation(global.TimeFormat, sma.Time, time.Local)
if err != nil {
return nil, errors.Fatalf("error in time option: %v", err)
}
@@ -291,7 +292,7 @@ func filterAndReplaceSnapshot(ctx context.Context, repo restic.Repository, sn *d
return true, nil
}
func runRewrite(ctx context.Context, opts RewriteOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runRewrite(ctx context.Context, opts RewriteOptions, gopts global.Options, args []string, term ui.Terminal) error {
if !opts.SnapshotSummary && opts.ExcludePatternOptions.Empty() && opts.Metadata.empty() {
return errors.Fatal("Nothing to do: no excludes provided and no new metadata provided")
}
+9 -8
View File
@@ -7,12 +7,13 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui"
)
func testRunRewriteExclude(t testing.TB, gopts GlobalOptions, excludes []string, forget bool, metadata snapshotMetadataArgs) {
func testRunRewriteExclude(t testing.TB, gopts global.Options, excludes []string, forget bool, metadata snapshotMetadataArgs) {
opts := RewriteOptions{
ExcludePatternOptions: filter.ExcludePatternOptions{
Excludes: excludes,
@@ -21,7 +22,7 @@ func testRunRewriteExclude(t testing.TB, gopts GlobalOptions, excludes []string,
Metadata: metadata,
}
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runRewrite(context.TODO(), opts, gopts, nil, gopts.Term)
}))
}
@@ -42,7 +43,7 @@ func getSnapshot(t testing.TB, snapshotID restic.ID, env *testEnvironment) *data
t.Helper()
var snapshots []*data.Snapshot
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -118,7 +119,7 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) {
testRunRewriteExclude(t, env.gopts, []string{}, true, metadata)
var snapshots []*data.Snapshot
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -132,7 +133,7 @@ func testRewriteMetadata(t *testing.T, metadata snapshotMetadataArgs) {
newSnapshot := snapshots[0]
if metadata.Time != "" {
rtest.Assert(t, newSnapshot.Time.Format(TimeFormat) == metadata.Time, "New snapshot should have time %s", metadata.Time)
rtest.Assert(t, newSnapshot.Time.Format(global.TimeFormat) == metadata.Time, "New snapshot should have time %s", metadata.Time)
}
if metadata.Hostname != "" {
@@ -158,7 +159,7 @@ func TestRewriteSnaphotSummary(t *testing.T) {
defer cleanup()
createBasicRewriteRepo(t, env)
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.Term)
}))
// no new snapshot should be created as the snapshot already has a summary
@@ -166,7 +167,7 @@ func TestRewriteSnaphotSummary(t *testing.T) {
// replace snapshot by one without a summary
var oldSummary *data.SnapshotSummary
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -183,7 +184,7 @@ func TestRewriteSnaphotSummary(t *testing.T) {
rtest.OK(t, err)
// rewrite snapshot and lookup ID of new snapshot
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runRewrite(context.TODO(), RewriteOptions{SnapshotSummary: true}, gopts, []string{}, gopts.Term)
}))
newSnapshots := testListSnapshots(t, env.gopts, 2)
+6 -5
View File
@@ -8,19 +8,20 @@ import (
"path/filepath"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/selfupdate"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *GlobalOptions) {
func registerSelfUpdateCommand(cmd *cobra.Command, globalOptions *global.Options) {
cmd.AddCommand(
newSelfUpdateCommand(globalOptions),
)
}
func newSelfUpdateCommand(globalOptions *GlobalOptions) *cobra.Command {
func newSelfUpdateCommand(globalOptions *global.Options) *cobra.Command {
var opts SelfUpdateOptions
cmd := &cobra.Command{
@@ -60,7 +61,7 @@ func (opts *SelfUpdateOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)")
}
func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts global.Options, args []string, term ui.Terminal) error {
if opts.Output == "" {
file, err := os.Executable()
if err != nil {
@@ -89,12 +90,12 @@ func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOpti
printer := ui.NewProgressPrinter(false, gopts.Verbosity, term)
printer.P("writing restic to %v", opts.Output)
v, err := selfupdate.DownloadLatestStableRelease(ctx, opts.Output, version, printer.P)
v, err := selfupdate.DownloadLatestStableRelease(ctx, opts.Output, global.Version, printer.P)
if err != nil {
return errors.Fatalf("unable to update restic: %v", err)
}
if v != version {
if v != global.Version {
printer.S("successfully updated restic to version %v", v)
}
+5 -2
View File
@@ -2,8 +2,11 @@
package main
import "github.com/spf13/cobra"
import (
"github.com/restic/restic/internal/global"
"github.com/spf13/cobra"
)
func registerSelfUpdateCommand(_ *cobra.Command, _ *GlobalOptions) {
func registerSelfUpdateCommand(_ *cobra.Command, _ *global.Options) {
// No commands to register in non-selfupdate mode
}
+4 -3
View File
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/table"
@@ -16,7 +17,7 @@ import (
"github.com/spf13/pflag"
)
func newSnapshotsCommand(globalOptions *GlobalOptions) *cobra.Command {
func newSnapshotsCommand(globalOptions *global.Options) *cobra.Command {
var opts SnapshotOptions
cmd := &cobra.Command{
@@ -68,7 +69,7 @@ func (opts *SnapshotOptions) AddFlags(f *pflag.FlagSet) {
f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma")
}
func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts global.Options, args []string, term ui.Terminal) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock, printer)
if err != nil {
@@ -242,7 +243,7 @@ func PrintSnapshots(stdout io.Writer, list data.Snapshots, reasons []data.KeepRe
for _, sn := range list {
data := snapshot{
ID: sn.ID().Str(),
Timestamp: sn.Time.Local().Format(TimeFormat),
Timestamp: sn.Time.Local().Format(global.TimeFormat),
Hostname: sn.Hostname,
Tags: sn.Tags,
Paths: sn.Paths,
+3 -2
View File
@@ -5,12 +5,13 @@ import (
"encoding/json"
"testing"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
func testRunSnapshots(t testing.TB, gopts GlobalOptions) (newest *Snapshot, snapmap map[restic.ID]Snapshot) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunSnapshots(t testing.TB, gopts global.Options) (newest *Snapshot, snapmap map[restic.ID]Snapshot) {
buf, err := withCaptureStdout(t, gopts, func(ctx context.Context, gopts global.Options) error {
gopts.JSON = true
opts := SnapshotOptions{}
+3 -2
View File
@@ -11,6 +11,7 @@ import (
"github.com/restic/chunker"
"github.com/restic/restic/internal/crypto"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/restorer"
@@ -23,7 +24,7 @@ import (
"github.com/spf13/pflag"
)
func newStatsCommand(globalOptions *GlobalOptions) *cobra.Command {
func newStatsCommand(globalOptions *global.Options) *cobra.Command {
var opts StatsOptions
cmd := &cobra.Command{
@@ -95,7 +96,7 @@ func must(err error) {
}
}
func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args []string, term ui.Terminal) error {
func runStats(ctx context.Context, opts StatsOptions, gopts global.Options, args []string, term ui.Terminal) error {
err := verifyStatsInput(opts)
if err != nil {
return err
+3 -2
View File
@@ -9,12 +9,13 @@ import (
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui"
)
func newTagCommand(globalOptions *GlobalOptions) *cobra.Command {
func newTagCommand(globalOptions *global.Options) *cobra.Command {
var opts TagOptions
cmd := &cobra.Command{
@@ -118,7 +119,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *data.Snaps
return changed, nil
}
func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, term ui.Terminal, args []string) error {
func runTag(ctx context.Context, opts TagOptions, gopts global.Options, term ui.Terminal, args []string) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 {
+3 -2
View File
@@ -5,11 +5,12 @@ import (
"testing"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
rtest "github.com/restic/restic/internal/test"
)
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
func testRunTag(t testing.TB, opts TagOptions, gopts global.Options) {
rtest.OK(t, withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
return runTag(context.TODO(), opts, gopts, gopts.Term, []string{})
}))
}
+4 -3
View File
@@ -3,13 +3,14 @@ package main
import (
"context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func newUnlockCommand(globalOptions *GlobalOptions) *cobra.Command {
func newUnlockCommand(globalOptions *global.Options) *cobra.Command {
var opts UnlockOptions
cmd := &cobra.Command{
@@ -43,9 +44,9 @@ func (opts *UnlockOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RemoveAll, "remove-all", false, "remove all locks, even non-stale ones")
}
func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions, term ui.Terminal) error {
func runUnlock(ctx context.Context, opts UnlockOptions, gopts global.Options, term ui.Terminal) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, term)
repo, err := OpenRepository(ctx, gopts, printer)
repo, err := global.OpenRepository(ctx, gopts, printer)
if err != nil {
return err
}
+4 -3
View File
@@ -4,11 +4,12 @@ import (
"encoding/json"
"runtime"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui"
"github.com/spf13/cobra"
)
func newVersionCommand(globalOptions *GlobalOptions) *cobra.Command {
func newVersionCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Print version information",
@@ -37,7 +38,7 @@ Exit status is 1 if there was any error.
jsonS := jsonVersion{
MessageType: "version",
Version: version,
Version: global.Version,
GoVersion: runtime.Version(),
GoOS: runtime.GOOS,
GoArch: runtime.GOARCH,
@@ -50,7 +51,7 @@ Exit status is 1 if there was any error.
}
} else {
printer.S("restic %s compiled with %v on %v/%v\n",
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
global.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
}
},
}
+3 -1
View File
@@ -3,12 +3,14 @@ package main
import (
"io"
"testing"
"github.com/restic/restic/internal/global"
)
// TestFlags checks for double defined flags, the commands will panic on
// ParseFlags() when a shorthand flag is defined twice.
func TestFlags(t *testing.T) {
for _, cmd := range newRootCommand(&GlobalOptions{}).Commands() {
for _, cmd := range newRootCommand(&global.Options{}).Commands() {
t.Run(cmd.Name(), func(t *testing.T) {
cmd.Flags().SetOutput(io.Discard)
err := cmd.ParseFlags([]string{"--help"})
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"os"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/ui"
)
@@ -43,6 +44,6 @@ func formatNode(path string, n *data.Node, long bool, human bool) string {
return fmt.Sprintf("%s %5d %5d %s %s %s%s",
mode|n.Mode, n.UID, n.GID, size,
n.ModTime.Local().Format(TimeFormat), path,
n.ModTime.Local().Format(global.TimeFormat), path,
target)
}
-512
View File
@@ -1,512 +0,0 @@
package main
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/cache"
"github.com/restic/restic/internal/backend/limiter"
"github.com/restic/restic/internal/backend/location"
"github.com/restic/restic/internal/backend/logger"
"github.com/restic/restic/internal/backend/retry"
"github.com/restic/restic/internal/backend/sema"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/options"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/textfile"
"github.com/restic/restic/internal/ui"
"github.com/restic/restic/internal/ui/progress"
"github.com/spf13/pflag"
"github.com/restic/restic/internal/errors"
)
// ErrNoRepository is used to report if opening a repository failed due
// to a missing backend storage location or config file
var ErrNoRepository = errors.New("repository does not exist")
var version = "0.18.1-dev (compiled manually)"
// TimeFormat is the format used for all timestamps printed by restic.
const TimeFormat = "2006-01-02 15:04:05"
type BackendWrapper func(r backend.Backend) (backend.Backend, error)
// GlobalOptions hold all global options for restic.
type GlobalOptions struct {
Repo string
RepositoryFile string
PasswordFile string
PasswordCommand string
KeyHint string
Quiet bool
Verbose int
NoLock bool
RetryLock time.Duration
JSON bool
CacheDir string
NoCache bool
CleanupCache bool
Compression repository.CompressionMode
PackSize uint
NoExtraVerify bool
InsecureNoPassword bool
backend.TransportOptions
limiter.Limits
Password string
Term ui.Terminal
Backends *location.Registry
BackendTestHook, BackendInnerTestHook BackendWrapper
// Verbosity is set as follows:
// 0 means: don't print any messages except errors, this is used when --quiet is specified
// 1 is the default: print essential messages
// 2 means: print more messages, report minor things, this is used when --verbose is specified
// 3 means: print very detailed debug messages, this is used when --verbose=2 is specified
Verbosity uint
Options []string
Extended options.Options
}
func (opts *GlobalOptions) AddFlags(f *pflag.FlagSet) {
f.StringVarP(&opts.Repo, "repo", "r", "", "`repository` to backup to or restore from (default: $RESTIC_REPOSITORY)")
f.StringVarP(&opts.RepositoryFile, "repository-file", "", "", "`file` to read the repository location from (default: $RESTIC_REPOSITORY_FILE)")
f.StringVarP(&opts.PasswordFile, "password-file", "p", "", "`file` to read the repository password from (default: $RESTIC_PASSWORD_FILE)")
f.StringVarP(&opts.KeyHint, "key-hint", "", "", "`key` ID of key to try decrypting first (default: $RESTIC_KEY_HINT)")
f.StringVarP(&opts.PasswordCommand, "password-command", "", "", "shell `command` to obtain the repository password from (default: $RESTIC_PASSWORD_COMMAND)")
f.BoolVarP(&opts.Quiet, "quiet", "q", false, "do not output comprehensive progress report")
// use empty parameter name as `-v, --verbose n` instead of the correct `--verbose=n` is confusing
f.CountVarP(&opts.Verbose, "verbose", "v", "be verbose (specify multiple times or a level using --verbose=n``, max level/times is 2)")
f.BoolVar(&opts.NoLock, "no-lock", false, "do not lock the repository, this allows some operations on read-only repositories")
f.DurationVar(&opts.RetryLock, "retry-lock", 0, "retry to lock the repository if it is already locked, takes a value like 5m or 2h (default: no retries)")
f.BoolVarP(&opts.JSON, "json", "", false, "set output mode to JSON for commands that support it")
f.StringVar(&opts.CacheDir, "cache-dir", "", "set the cache `directory`. (default: use system default cache directory)")
f.BoolVar(&opts.NoCache, "no-cache", false, "do not use a local cache")
f.StringSliceVar(&opts.RootCertFilenames, "cacert", nil, "`file` to load root certificates from (default: use system certificates or $RESTIC_CACERT)")
f.StringVar(&opts.TLSClientCertKeyFilename, "tls-client-cert", "", "path to a `file` containing PEM encoded TLS client certificate and private key (default: $RESTIC_TLS_CLIENT_CERT)")
f.BoolVar(&opts.InsecureNoPassword, "insecure-no-password", false, "use an empty password for the repository, must be passed to every restic command (insecure)")
f.BoolVar(&opts.InsecureTLS, "insecure-tls", false, "skip TLS certificate verification when connecting to the repository (insecure)")
f.BoolVar(&opts.CleanupCache, "cleanup-cache", false, "auto remove old cache directories")
f.Var(&opts.Compression, "compression", "compression mode (only available for repository format version 2), one of (auto|off|fastest|better|max) (default: $RESTIC_COMPRESSION)")
f.BoolVar(&opts.NoExtraVerify, "no-extra-verify", false, "skip additional verification of data before upload (see documentation)")
f.IntVar(&opts.Limits.UploadKb, "limit-upload", 0, "limits uploads to a maximum `rate` in KiB/s. (default: unlimited)")
f.IntVar(&opts.Limits.DownloadKb, "limit-download", 0, "limits downloads to a maximum `rate` in KiB/s. (default: unlimited)")
f.UintVar(&opts.PackSize, "pack-size", 0, "set target pack `size` in MiB, created pack files may be larger (default: $RESTIC_PACK_SIZE)")
f.StringSliceVarP(&opts.Options, "option", "o", []string{}, "set extended option (`key=value`, can be specified multiple times)")
f.StringVar(&opts.HTTPUserAgent, "http-user-agent", "", "set a http user agent for outgoing http requests")
f.DurationVar(&opts.StuckRequestTimeout, "stuck-request-timeout", 5*time.Minute, "`duration` after which to retry stuck requests")
opts.Repo = os.Getenv("RESTIC_REPOSITORY")
opts.RepositoryFile = os.Getenv("RESTIC_REPOSITORY_FILE")
opts.PasswordFile = os.Getenv("RESTIC_PASSWORD_FILE")
opts.KeyHint = os.Getenv("RESTIC_KEY_HINT")
opts.PasswordCommand = os.Getenv("RESTIC_PASSWORD_COMMAND")
if os.Getenv("RESTIC_CACERT") != "" {
opts.RootCertFilenames = strings.Split(os.Getenv("RESTIC_CACERT"), ",")
}
opts.TLSClientCertKeyFilename = os.Getenv("RESTIC_TLS_CLIENT_CERT")
comp := os.Getenv("RESTIC_COMPRESSION")
if comp != "" {
// ignore error as there's no good way to handle it
_ = opts.Compression.Set(comp)
}
// parse target pack size from env, on error the default value will be used
targetPackSize, _ := strconv.ParseUint(os.Getenv("RESTIC_PACK_SIZE"), 10, 32)
opts.PackSize = uint(targetPackSize)
if os.Getenv("RESTIC_HTTP_USER_AGENT") != "" {
opts.HTTPUserAgent = os.Getenv("RESTIC_HTTP_USER_AGENT")
}
}
func (opts *GlobalOptions) PreRun(needsPassword bool) error {
// set verbosity, default is one
opts.Verbosity = 1
if opts.Quiet && opts.Verbose > 0 {
return errors.Fatal("--quiet and --verbose cannot be specified at the same time")
}
switch {
case opts.Verbose >= 2:
opts.Verbosity = 3
case opts.Verbose > 0:
opts.Verbosity = 2
case opts.Quiet:
opts.Verbosity = 0
}
// parse extended options
extendedOpts, err := options.Parse(opts.Options)
if err != nil {
return err
}
opts.Extended = extendedOpts
if !needsPassword {
return nil
}
pwd, err := resolvePassword(opts, "RESTIC_PASSWORD")
if err != nil {
return errors.Fatalf("Resolving password failed: %v", err)
}
opts.Password = pwd
return nil
}
// resolvePassword determines the password to be used for opening the repository.
func resolvePassword(opts *GlobalOptions, envStr string) (string, error) {
if opts.PasswordFile != "" && opts.PasswordCommand != "" {
return "", errors.Fatalf("Password file and command are mutually exclusive options")
}
if opts.PasswordCommand != "" {
args, err := backend.SplitShellStrings(opts.PasswordCommand)
if err != nil {
return "", err
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Stderr = os.Stderr
output, err := cmd.Output()
if err != nil {
return "", err
}
return strings.TrimSpace(string(output)), nil
}
if opts.PasswordFile != "" {
return loadPasswordFromFile(opts.PasswordFile)
}
if pwd := os.Getenv(envStr); pwd != "" {
return pwd, nil
}
return "", nil
}
// loadPasswordFromFile loads a password from a file while stripping a BOM and
// converting the password to UTF-8.
func loadPasswordFromFile(pwdFile string) (string, error) {
s, err := textfile.Read(pwdFile)
if errors.Is(err, os.ErrNotExist) {
return "", errors.Fatalf("%s does not exist", pwdFile)
}
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile")
}
// ReadPassword reads the password from a password file, the environment
// variable RESTIC_PASSWORD or prompts the user. If the context is canceled,
// the function leaks the password reading goroutine.
func ReadPassword(ctx context.Context, gopts GlobalOptions, prompt string) (string, error) {
if gopts.InsecureNoPassword {
if gopts.Password != "" {
return "", errors.Fatal("--insecure-no-password must not be specified together with providing a password via a cli option or environment variable")
}
return "", nil
}
if gopts.Password != "" {
return gopts.Password, nil
}
password, err := gopts.Term.ReadPassword(ctx, prompt)
if err != nil {
return "", fmt.Errorf("unable to read password: %w", err)
}
if len(password) == 0 {
return "", errors.Fatal("an empty password is not allowed by default. Pass the flag `--insecure-no-password` to restic to disable this check")
}
return password, nil
}
// ReadPasswordTwice calls ReadPassword two times and returns an error when the
// passwords don't match. If the context is canceled, the function leaks the
// password reading goroutine.
func ReadPasswordTwice(ctx context.Context, gopts GlobalOptions, prompt1, prompt2 string) (string, error) {
pw1, err := ReadPassword(ctx, gopts, prompt1)
if err != nil {
return "", err
}
if gopts.Term.InputIsTerminal() {
pw2, err := ReadPassword(ctx, gopts, prompt2)
if err != nil {
return "", err
}
if pw1 != pw2 {
return "", errors.Fatal("passwords do not match")
}
}
return pw1, nil
}
func ReadRepo(gopts GlobalOptions) (string, error) {
if gopts.Repo == "" && gopts.RepositoryFile == "" {
return "", errors.Fatal("Please specify repository location (-r or --repository-file)")
}
repo := gopts.Repo
if gopts.RepositoryFile != "" {
if repo != "" {
return "", errors.Fatal("Options -r and --repository-file are mutually exclusive, please specify only one")
}
s, err := textfile.Read(gopts.RepositoryFile)
if errors.Is(err, os.ErrNotExist) {
return "", errors.Fatalf("%s does not exist", gopts.RepositoryFile)
}
if err != nil {
return "", err
}
repo = strings.TrimSpace(string(s))
}
return repo, nil
}
const maxKeys = 20
// OpenRepository reads the password and opens the repository.
func OpenRepository(ctx context.Context, gopts GlobalOptions, printer progress.Printer) (*repository.Repository, error) {
repo, err := ReadRepo(gopts)
if err != nil {
return nil, err
}
be, err := open(ctx, repo, gopts, gopts.Extended, printer)
if err != nil {
return nil, err
}
s, err := repository.New(be, repository.Options{
Compression: gopts.Compression,
PackSize: gopts.PackSize * 1024 * 1024,
NoExtraVerify: gopts.NoExtraVerify,
})
if err != nil {
return nil, errors.Fatalf("%s", err)
}
passwordTriesLeft := 1
if gopts.Term.InputIsTerminal() && gopts.Password == "" && !gopts.InsecureNoPassword {
passwordTriesLeft = 3
}
for ; passwordTriesLeft > 0; passwordTriesLeft-- {
gopts.Password, err = ReadPassword(ctx, gopts, "enter password for repository: ")
if ctx.Err() != nil {
return nil, ctx.Err()
}
if err != nil && passwordTriesLeft > 1 {
gopts.Password = ""
printer.E("%s. Try again", err)
}
if err != nil {
continue
}
err = s.SearchKey(ctx, gopts.Password, maxKeys, gopts.KeyHint)
if err != nil && passwordTriesLeft > 1 {
gopts.Password = ""
printer.E("%s. Try again", err)
}
}
if err != nil {
if errors.IsFatal(err) || errors.Is(err, repository.ErrNoKeyFound) {
return nil, err
}
return nil, errors.Fatalf("%s", err)
}
id := s.Config().ID
if len(id) > 8 {
id = id[:8]
}
extra := ""
if s.Config().Version >= 2 {
extra = ", compression level " + gopts.Compression.String()
}
printer.PT("repository %v opened (version %v%s)", id, s.Config().Version, extra)
if gopts.NoCache {
return s, nil
}
c, err := cache.New(s.Config().ID, gopts.CacheDir)
if err != nil {
printer.E("unable to open cache: %v", err)
return s, nil
}
if c.Created {
printer.PT("created new cache in %v", c.Base)
}
// start using the cache
s.UseCache(c, printer.E)
oldCacheDirs, err := cache.Old(c.Base)
if err != nil {
printer.E("unable to find old cache directories: %v", err)
}
// nothing more to do if no old cache dirs could be found
if len(oldCacheDirs) == 0 {
return s, nil
}
// cleanup old cache dirs if instructed to do so
if gopts.CleanupCache {
printer.PT("removing %d old cache dirs from %v", len(oldCacheDirs), c.Base)
for _, item := range oldCacheDirs {
dir := filepath.Join(c.Base, item.Name())
err = os.RemoveAll(dir)
if err != nil {
printer.E("unable to remove %v: %v", dir, err)
}
}
} else {
printer.PT("found %d old cache directories in %v, run `restic cache --cleanup` to remove them",
len(oldCacheDirs), c.Base)
}
return s, nil
}
func parseConfig(loc location.Location, opts options.Options) (interface{}, error) {
cfg := loc.Config
if cfg, ok := cfg.(backend.ApplyEnvironmenter); ok {
cfg.ApplyEnvironment("")
}
// only apply options for a particular backend here
opts = opts.Extract(loc.Scheme)
if err := opts.Apply(loc.Scheme, cfg); err != nil {
return nil, err
}
debug.Log("opening %v repository at %#v", loc.Scheme, cfg)
return cfg, nil
}
func innerOpen(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, create bool, printer progress.Printer) (backend.Backend, error) {
debug.Log("parsing location %v", location.StripPassword(gopts.Backends, s))
loc, err := location.Parse(gopts.Backends, s)
if err != nil {
return nil, errors.Fatalf("parsing repository location failed: %v", err)
}
cfg, err := parseConfig(loc, opts)
if err != nil {
return nil, err
}
rt, err := backend.Transport(gopts.TransportOptions)
if err != nil {
return nil, errors.Fatalf("%s", err)
}
// wrap the transport so that the throughput via HTTP is limited
lim := limiter.NewStaticLimiter(gopts.Limits)
rt = lim.Transport(rt)
factory := gopts.Backends.Lookup(loc.Scheme)
if factory == nil {
return nil, errors.Fatalf("invalid backend: %q", loc.Scheme)
}
var be backend.Backend
if create {
be, err = factory.Create(ctx, cfg, rt, lim, printer.E)
} else {
be, err = factory.Open(ctx, cfg, rt, lim, printer.E)
}
if errors.Is(err, backend.ErrNoRepository) {
//nolint:staticcheck // capitalized error string is intentional
return nil, fmt.Errorf("Fatal: %w at %v: %v", ErrNoRepository, location.StripPassword(gopts.Backends, s), err)
}
if err != nil {
if create {
// init already wraps the error message
return nil, err
}
return nil, errors.Fatalf("unable to open repository at %v: %v", location.StripPassword(gopts.Backends, s), err)
}
// wrap with debug logging and connection limiting
be = logger.New(sema.NewBackend(be))
// wrap backend if a test specified an inner hook
if gopts.BackendInnerTestHook != nil {
be, err = gopts.BackendInnerTestHook(be)
if err != nil {
return nil, err
}
}
report := func(msg string, err error, d time.Duration) {
if d >= 0 {
printer.E("%v returned error, retrying after %v: %v", msg, d, err)
} else {
printer.E("%v failed: %v", msg, err)
}
}
success := func(msg string, retries int) {
printer.E("%v operation successful after %d retries", msg, retries)
}
be = retry.New(be, 15*time.Minute, report, success)
// wrap backend if a test specified a hook
if gopts.BackendTestHook != nil {
be, err = gopts.BackendTestHook(be)
if err != nil {
return nil, err
}
}
return be, nil
}
// Open the backend specified by a location config.
func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, printer progress.Printer) (backend.Backend, error) {
be, err := innerOpen(ctx, s, gopts, opts, false, printer)
if err != nil {
return nil, err
}
// check if config is there
fi, err := be.Stat(ctx, backend.Handle{Type: restic.ConfigFile})
if be.IsNotExist(err) {
//nolint:staticcheck // capitalized error string is intentional
return nil, fmt.Errorf("Fatal: %w: unable to open config file: %v\nIs there a repository at the following location?\n%v", ErrNoRepository, err, location.StripPassword(gopts.Backends, s))
}
if err != nil {
return nil, errors.Fatalf("unable to open config file: %v\nIs there a repository at the following location?\n%v", err, location.StripPassword(gopts.Backends, s))
}
if fi.Size == 0 {
return nil, errors.New("config file has zero size, invalid repository?")
}
return be, nil
}
// Create the backend specified by URI.
func create(ctx context.Context, s string, gopts GlobalOptions, opts options.Options, printer progress.Printer) (backend.Backend, error) {
return innerOpen(ctx, s, gopts, opts, true, printer)
}
-126
View File
@@ -1,126 +0,0 @@
//go:build debug || profile
// +build debug profile
package main
import (
"fmt"
"io"
"net/http"
_ "net/http/pprof"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/pkg/profile"
)
func registerProfiling(cmd *cobra.Command, stderr io.Writer) {
var profiler profiler
origPreRun := cmd.PersistentPreRunE
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if origPreRun != nil {
if err := origPreRun(cmd, args); err != nil {
return err
}
}
return profiler.Start(profiler.opts, stderr)
}
// Once https://github.com/spf13/cobra/issues/1893 is fixed,
// this could use PersistentPostRunE instead of OnFinalize,
// reverting https://github.com/restic/restic/pull/5373.
cobra.OnFinalize(func() {
profiler.Stop()
})
profiler.opts.AddFlags(cmd.PersistentFlags())
}
type profiler struct {
opts ProfileOptions
stop interface {
Stop()
}
}
type ProfileOptions struct {
listen string
memPath string
cpuPath string
tracePath string
blockPath string
insecure bool
}
func (opts *ProfileOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.listen, "listen-profile", "", "listen on this `address:port` for memory profiling")
f.StringVar(&opts.memPath, "mem-profile", "", "write memory profile to `dir`")
f.StringVar(&opts.cpuPath, "cpu-profile", "", "write cpu profile to `dir`")
f.StringVar(&opts.tracePath, "trace-profile", "", "write trace to `dir`")
f.StringVar(&opts.blockPath, "block-profile", "", "write block profile to `dir`")
f.BoolVar(&opts.insecure, "insecure-kdf", false, "use insecure KDF settings")
}
type fakeTestingTB struct {
stderr io.Writer
}
func (t fakeTestingTB) Logf(msg string, args ...interface{}) {
fmt.Fprintf(t.stderr, msg, args...)
}
func (p *profiler) Start(profileOpts ProfileOptions, stderr io.Writer) error {
if profileOpts.listen != "" {
fmt.Fprintf(stderr, "running profile HTTP server on %v\n", profileOpts.listen)
go func() {
err := http.ListenAndServe(profileOpts.listen, nil)
if err != nil {
fmt.Fprintf(stderr, "profile HTTP server listen failed: %v\n", err)
}
}()
}
profilesEnabled := 0
if profileOpts.memPath != "" {
profilesEnabled++
}
if profileOpts.cpuPath != "" {
profilesEnabled++
}
if profileOpts.tracePath != "" {
profilesEnabled++
}
if profileOpts.blockPath != "" {
profilesEnabled++
}
if profilesEnabled > 1 {
return errors.Fatal("only one profile (memory, CPU, trace, or block) may be activated at the same time")
}
if profileOpts.memPath != "" {
p.stop = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.MemProfile, profile.ProfilePath(profileOpts.memPath))
} else if profileOpts.cpuPath != "" {
p.stop = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.CPUProfile, profile.ProfilePath(profileOpts.cpuPath))
} else if profileOpts.tracePath != "" {
p.stop = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.TraceProfile, profile.ProfilePath(profileOpts.tracePath))
} else if profileOpts.blockPath != "" {
p.stop = profile.Start(profile.Quiet, profile.NoShutdownHook, profile.BlockProfile, profile.ProfilePath(profileOpts.blockPath))
}
if profileOpts.insecure {
repository.TestUseLowSecurityKDFParameters(fakeTestingTB{stderr})
}
return nil
}
func (p *profiler) Stop() {
if p.stop != nil {
p.stop.Stop()
}
}
-14
View File
@@ -1,14 +0,0 @@
//go:build !debug && !profile
// +build !debug,!profile
package main
import (
"io"
"github.com/spf13/cobra"
)
func registerProfiling(_ *cobra.Command, _ io.Writer) {
// No profiling in release mode
}
-51
View File
@@ -1,51 +0,0 @@
package main
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
rtest "github.com/restic/restic/internal/test"
)
func TestReadRepo(t *testing.T) {
tempDir := rtest.TempDir(t)
// test --repo option
var gopts GlobalOptions
gopts.Repo = tempDir
repo, err := ReadRepo(gopts)
rtest.OK(t, err)
rtest.Equals(t, tempDir, repo)
// test --repository-file option
foo := filepath.Join(tempDir, "foo")
err = os.WriteFile(foo, []byte(tempDir+"\n"), 0666)
rtest.OK(t, err)
var gopts2 GlobalOptions
gopts2.RepositoryFile = foo
repo, err = ReadRepo(gopts2)
rtest.OK(t, err)
rtest.Equals(t, tempDir, repo)
var gopts3 GlobalOptions
gopts3.RepositoryFile = foo + "-invalid"
_, err = ReadRepo(gopts3)
if err == nil {
t.Fatal("must not read repository path from invalid file path")
}
}
func TestReadEmptyPassword(t *testing.T) {
opts := GlobalOptions{InsecureNoPassword: true}
password, err := ReadPassword(context.TODO(), opts, "test")
rtest.OK(t, err)
rtest.Equals(t, "", password, "got unexpected password")
opts.Password = "invalid"
_, err = ReadPassword(context.TODO(), opts, "test")
rtest.Assert(t, strings.Contains(err.Error(), "must not be specified together with providing a password via a cli option or environment variable"), "unexpected error message, got %v", err)
}
+18 -17
View File
@@ -18,6 +18,7 @@ import (
"github.com/restic/restic/internal/backend/retry"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/options"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
@@ -169,7 +170,7 @@ func dirStats(t testing.TB, dir string) (stat dirStat) {
type testEnvironment struct {
base, cache, repo, mountpoint, testdata string
gopts GlobalOptions
gopts global.Options
}
type logOutputter struct {
@@ -209,7 +210,7 @@ func withTestEnvironment(t testing.TB) (env *testEnvironment, cleanup func()) {
rtest.OK(t, os.MkdirAll(env.cache, 0700))
rtest.OK(t, os.MkdirAll(env.repo, 0700))
env.gopts = GlobalOptions{
env.gopts = global.Options{
Repo: env.repo,
Quiet: true,
CacheDir: env.cache,
@@ -240,9 +241,9 @@ func testSetupBackupData(t testing.TB, env *testEnvironment) string {
return datafile
}
func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
func listPacks(gopts global.Options, t *testing.T) restic.IDSet {
var packs restic.IDSet
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -259,9 +260,9 @@ func listPacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
return packs
}
func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
func listTreePacks(gopts global.Options, t *testing.T) restic.IDSet {
var treePacks restic.IDSet
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, r, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -279,7 +280,7 @@ func listTreePacks(gopts GlobalOptions, t *testing.T) restic.IDSet {
return treePacks
}
func captureBackend(gopts *GlobalOptions) func() backend.Backend {
func captureBackend(gopts *global.Options) func() backend.Backend {
var be backend.Backend
gopts.BackendTestHook = func(r backend.Backend) (backend.Backend, error) {
be = r
@@ -290,9 +291,9 @@ func captureBackend(gopts *GlobalOptions) func() backend.Backend {
}
}
func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
func removePacks(gopts global.Options, t testing.TB, remove restic.IDSet) {
be := captureBackend(&gopts)
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, _, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -306,9 +307,9 @@ func removePacks(gopts GlobalOptions, t testing.TB, remove restic.IDSet) {
rtest.OK(t, err)
}
func removePacksExcept(gopts GlobalOptions, t testing.TB, keep restic.IDSet, removeTreePacks bool) {
func removePacksExcept(gopts global.Options, t testing.TB, keep restic.IDSet, removeTreePacks bool) {
be := captureBackend(&gopts)
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, r, unlock, err := openWithExclusiveLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -345,7 +346,7 @@ func includes(haystack []string, needle string) bool {
return false
}
func loadSnapshotMap(t testing.TB, gopts GlobalOptions) map[string]struct{} {
func loadSnapshotMap(t testing.TB, gopts global.Options) map[string]struct{} {
snapshotIDs := testRunList(t, gopts, "snapshots")
m := make(map[string]struct{})
@@ -367,9 +368,9 @@ func lastSnapshot(old, new map[string]struct{}) (map[string]struct{}, string) {
return old, ""
}
func testLoadSnapshot(t testing.TB, gopts GlobalOptions, id restic.ID) *data.Snapshot {
func testLoadSnapshot(t testing.TB, gopts global.Options, id restic.ID) *data.Snapshot {
var snapshot *data.Snapshot
err := withTermStatus(t, gopts, func(ctx context.Context, gopts GlobalOptions) error {
err := withTermStatus(t, gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
_, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err)
@@ -414,20 +415,20 @@ func testFileSize(filename string, size int64) error {
return nil
}
func withCaptureStdout(t testing.TB, gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) (*bytes.Buffer, error) {
func withCaptureStdout(t testing.TB, gopts global.Options, callback func(ctx context.Context, gopts global.Options) error) (*bytes.Buffer, error) {
buf := bytes.NewBuffer(nil)
err := withTermStatusRaw(os.Stdin, buf, &logOutputter{t: t}, gopts, callback)
return buf, err
}
func withTermStatus(t testing.TB, gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) error {
func withTermStatus(t testing.TB, gopts global.Options, callback func(ctx context.Context, gopts global.Options) error) error {
// stdout and stderr are written to by printer functions etc. That is the written data
// usually consists of one or multiple lines and therefore can be handled well
// by t.Log.
return withTermStatusRaw(os.Stdin, &logOutputter{t: t}, &logOutputter{t: t}, gopts, callback)
}
func withTermStatusRaw(stdin io.ReadCloser, stdout, stderr io.Writer, gopts GlobalOptions, callback func(ctx context.Context, gopts GlobalOptions) error) error {
func withTermStatusRaw(stdin io.ReadCloser, stdout, stderr io.Writer, gopts global.Options, callback func(ctx context.Context, gopts global.Options) error) error {
ctx, cancel := context.WithCancel(context.TODO())
var wg sync.WaitGroup
+5 -4
View File
@@ -11,6 +11,7 @@ import (
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/data"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui"
@@ -88,14 +89,14 @@ func TestListOnce(t *testing.T) {
createPrunableRepo(t, env)
testRunPrune(t, env.gopts, pruneOpts)
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
_, err := runCheck(context.TODO(), checkOpts, gopts, nil, gopts.Term)
return err
}))
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runRebuildIndex(context.TODO(), RepairIndexOptions{}, gopts, gopts.Term)
}))
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
return runRebuildIndex(context.TODO(), RepairIndexOptions{ReadAllPacks: true}, gopts, gopts.Term)
}))
}
@@ -163,7 +164,7 @@ func TestFindListOnce(t *testing.T) {
thirdSnapshot := restic.NewIDSet(testListSnapshots(t, env.gopts, 3)...)
var snapshotIDs restic.IDSet
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts GlobalOptions) error {
rtest.OK(t, withTermStatus(t, env.gopts, func(ctx context.Context, gopts global.Options) error {
printer := ui.NewProgressPrinter(gopts.JSON, gopts.Verbosity, gopts.Term)
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, false, printer)
rtest.OK(t, err)
+6 -5
View File
@@ -3,12 +3,13 @@ package main
import (
"context"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/ui/progress"
)
func internalOpenWithLocked(ctx context.Context, gopts GlobalOptions, dryRun bool, exclusive bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
repo, err := OpenRepository(ctx, gopts, printer)
func internalOpenWithLocked(ctx context.Context, gopts global.Options, dryRun bool, exclusive bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
repo, err := global.OpenRepository(ctx, gopts, printer)
if err != nil {
return nil, nil, nil, err
}
@@ -34,16 +35,16 @@ func internalOpenWithLocked(ctx context.Context, gopts GlobalOptions, dryRun boo
return ctx, repo, unlock, nil
}
func openWithReadLock(ctx context.Context, gopts GlobalOptions, noLock bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
func openWithReadLock(ctx context.Context, gopts global.Options, noLock bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
// TODO enforce read-only operations once the locking code has moved to the repository
return internalOpenWithLocked(ctx, gopts, noLock, false, printer)
}
func openWithAppendLock(ctx context.Context, gopts GlobalOptions, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
func openWithAppendLock(ctx context.Context, gopts global.Options, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
// TODO enforce non-exclusive operations once the locking code has moved to the repository
return internalOpenWithLocked(ctx, gopts, dryRun, false, printer)
}
func openWithExclusiveLock(ctx context.Context, gopts GlobalOptions, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
func openWithExclusiveLock(ctx context.Context, gopts global.Options, dryRun bool, printer progress.Printer) (context.Context, *repository.Repository, func(), error) {
return internalOpenWithLocked(ctx, gopts, dryRun, true, printer)
}
+7 -6
View File
@@ -18,6 +18,7 @@ import (
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/feature"
"github.com/restic/restic/internal/global"
"github.com/restic/restic/internal/repository"
"github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/ui/termstatus"
@@ -33,7 +34,7 @@ var ErrOK = errors.New("ok")
var cmdGroupDefault = "default"
var cmdGroupAdvanced = "advanced"
func newRootCommand(globalOptions *GlobalOptions) *cobra.Command {
func newRootCommand(globalOptions *global.Options) *cobra.Command {
cmd := &cobra.Command{
Use: "restic",
Short: "Backup and restore files",
@@ -103,7 +104,7 @@ The full documentation can be found at https://restic.readthedocs.io/ .
registerDebugCommand(cmd, globalOptions)
registerMountCommand(cmd, globalOptions)
registerSelfUpdateCommand(cmd, globalOptions)
registerProfiling(cmd, os.Stderr)
global.RegisterProfiling(cmd, os.Stderr)
return cmd
}
@@ -128,7 +129,7 @@ func tweakGoGC() {
}
}
func printExitError(globalOptions GlobalOptions, code int, message string) {
func printExitError(globalOptions global.Options, code int, message string) {
if globalOptions.JSON {
type jsonExitError struct {
MessageType string `json:"message_type"` // exit_error
@@ -171,9 +172,9 @@ func main() {
debug.Log("main %#v", os.Args)
debug.Log("restic %s compiled with %v on %v/%v",
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
global.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
globalOptions := GlobalOptions{
globalOptions := global.Options{
Backends: all.Backends(),
}
func() {
@@ -221,7 +222,7 @@ func main() {
exitCode = 3
case errors.Is(err, ErrFailedToRemoveOneOrMoreSnapshots):
exitCode = 3
case errors.Is(err, ErrNoRepository):
case errors.Is(err, global.ErrNoRepository):
exitCode = 10
case restic.IsAlreadyLocked(err):
exitCode = 11
-123
View File
@@ -1,123 +0,0 @@
package main
import (
"context"
"os"
"github.com/restic/restic/internal/errors"
"github.com/spf13/pflag"
)
type secondaryRepoOptions struct {
password string
// from-repo options
Repo string
RepositoryFile string
PasswordFile string
PasswordCommand string
KeyHint string
InsecureNoPassword bool
// repo2 options
LegacyRepo string
LegacyRepositoryFile string
LegacyPasswordFile string
LegacyPasswordCommand string
LegacyKeyHint string
}
func (opts *secondaryRepoOptions) AddFlags(f *pflag.FlagSet, repoPrefix string, repoUsage string) {
f.StringVarP(&opts.LegacyRepo, "repo2", "", "", repoPrefix+" `repository` "+repoUsage+" (default: $RESTIC_REPOSITORY2)")
f.StringVarP(&opts.LegacyRepositoryFile, "repository-file2", "", "", "`file` from which to read the "+repoPrefix+" repository location "+repoUsage+" (default: $RESTIC_REPOSITORY_FILE2)")
f.StringVarP(&opts.LegacyPasswordFile, "password-file2", "", "", "`file` to read the "+repoPrefix+" repository password from (default: $RESTIC_PASSWORD_FILE2)")
f.StringVarP(&opts.LegacyKeyHint, "key-hint2", "", "", "key ID of key to try decrypting the "+repoPrefix+" repository first (default: $RESTIC_KEY_HINT2)")
f.StringVarP(&opts.LegacyPasswordCommand, "password-command2", "", "", "shell `command` to obtain the "+repoPrefix+" repository password from (default: $RESTIC_PASSWORD_COMMAND2)")
// hide repo2 options
_ = f.MarkDeprecated("repo2", "use --repo or --from-repo instead")
_ = f.MarkDeprecated("repository-file2", "use --repository-file or --from-repository-file instead")
_ = f.MarkHidden("password-file2")
_ = f.MarkHidden("key-hint2")
_ = f.MarkHidden("password-command2")
opts.LegacyRepo = os.Getenv("RESTIC_REPOSITORY2")
opts.LegacyRepositoryFile = os.Getenv("RESTIC_REPOSITORY_FILE2")
opts.LegacyPasswordFile = os.Getenv("RESTIC_PASSWORD_FILE2")
opts.LegacyKeyHint = os.Getenv("RESTIC_KEY_HINT2")
opts.LegacyPasswordCommand = os.Getenv("RESTIC_PASSWORD_COMMAND2")
f.StringVarP(&opts.Repo, "from-repo", "", "", "source `repository` "+repoUsage+" (default: $RESTIC_FROM_REPOSITORY)")
f.StringVarP(&opts.RepositoryFile, "from-repository-file", "", "", "`file` from which to read the source repository location "+repoUsage+" (default: $RESTIC_FROM_REPOSITORY_FILE)")
f.StringVarP(&opts.PasswordFile, "from-password-file", "", "", "`file` to read the source repository password from (default: $RESTIC_FROM_PASSWORD_FILE)")
f.StringVarP(&opts.KeyHint, "from-key-hint", "", "", "key ID of key to try decrypting the source repository first (default: $RESTIC_FROM_KEY_HINT)")
f.StringVarP(&opts.PasswordCommand, "from-password-command", "", "", "shell `command` to obtain the source repository password from (default: $RESTIC_FROM_PASSWORD_COMMAND)")
f.BoolVar(&opts.InsecureNoPassword, "from-insecure-no-password", false, "use an empty password for the source repository (insecure)")
opts.Repo = os.Getenv("RESTIC_FROM_REPOSITORY")
opts.RepositoryFile = os.Getenv("RESTIC_FROM_REPOSITORY_FILE")
opts.PasswordFile = os.Getenv("RESTIC_FROM_PASSWORD_FILE")
opts.KeyHint = os.Getenv("RESTIC_FROM_KEY_HINT")
opts.PasswordCommand = os.Getenv("RESTIC_FROM_PASSWORD_COMMAND")
}
func fillSecondaryGlobalOpts(ctx context.Context, opts secondaryRepoOptions, gopts GlobalOptions, repoPrefix string) (GlobalOptions, bool, error) {
if opts.Repo == "" && opts.RepositoryFile == "" && opts.LegacyRepo == "" && opts.LegacyRepositoryFile == "" {
return GlobalOptions{}, false, errors.Fatal("Please specify a source repository location (--from-repo or --from-repository-file)")
}
hasFromRepo := opts.Repo != "" || opts.RepositoryFile != "" || opts.PasswordFile != "" ||
opts.KeyHint != "" || opts.PasswordCommand != "" || opts.InsecureNoPassword
hasRepo2 := opts.LegacyRepo != "" || opts.LegacyRepositoryFile != "" || opts.LegacyPasswordFile != "" ||
opts.LegacyKeyHint != "" || opts.LegacyPasswordCommand != ""
if hasFromRepo && hasRepo2 {
return GlobalOptions{}, false, errors.Fatal("Option groups repo2 and from-repo are mutually exclusive, please specify only one")
}
var err error
dstGopts := gopts
var pwdEnv string
if hasFromRepo {
if opts.Repo != "" && opts.RepositoryFile != "" {
return GlobalOptions{}, false, errors.Fatal("Options --from-repo and --from-repository-file are mutually exclusive, please specify only one")
}
dstGopts.Repo = opts.Repo
dstGopts.RepositoryFile = opts.RepositoryFile
dstGopts.PasswordFile = opts.PasswordFile
dstGopts.PasswordCommand = opts.PasswordCommand
dstGopts.KeyHint = opts.KeyHint
dstGopts.InsecureNoPassword = opts.InsecureNoPassword
pwdEnv = "RESTIC_FROM_PASSWORD"
repoPrefix = "source"
} else {
if opts.LegacyRepo != "" && opts.LegacyRepositoryFile != "" {
return GlobalOptions{}, false, errors.Fatal("Options --repo2 and --repository-file2 are mutually exclusive, please specify only one")
}
dstGopts.Repo = opts.LegacyRepo
dstGopts.RepositoryFile = opts.LegacyRepositoryFile
dstGopts.PasswordFile = opts.LegacyPasswordFile
dstGopts.PasswordCommand = opts.LegacyPasswordCommand
dstGopts.KeyHint = opts.LegacyKeyHint
// keep existing bevhaior for legacy options
dstGopts.InsecureNoPassword = false
pwdEnv = "RESTIC_PASSWORD2"
}
if opts.password != "" {
dstGopts.Password = opts.password
} else {
dstGopts.Password, err = resolvePassword(&dstGopts, pwdEnv)
if err != nil {
return GlobalOptions{}, false, err
}
}
dstGopts.Password, err = ReadPassword(ctx, dstGopts, "enter password for "+repoPrefix+" repository: ")
if err != nil {
return GlobalOptions{}, false, err
}
return dstGopts, hasFromRepo, nil
}
-179
View File
@@ -1,179 +0,0 @@
package main
import (
"context"
"os"
"path/filepath"
"testing"
rtest "github.com/restic/restic/internal/test"
)
// TestFillSecondaryGlobalOpts tests valid and invalid data on fillSecondaryGlobalOpts-function
func TestFillSecondaryGlobalOpts(t *testing.T) {
//secondaryRepoTestCase defines a struct for test cases
type secondaryRepoTestCase struct {
Opts secondaryRepoOptions
DstGOpts GlobalOptions
FromRepo bool
}
//validSecondaryRepoTestCases is a list with test cases that must pass
var validSecondaryRepoTestCases = []secondaryRepoTestCase{
{
// Test if Repo and Password are parsed correctly.
Opts: secondaryRepoOptions{
Repo: "backupDst",
password: "secretDst",
},
DstGOpts: GlobalOptions{
Repo: "backupDst",
Password: "secretDst",
},
FromRepo: true,
},
{
// Test if RepositoryFile and PasswordFile are parsed correctly.
Opts: secondaryRepoOptions{
RepositoryFile: "backupDst",
PasswordFile: "passwordFileDst",
},
DstGOpts: GlobalOptions{
RepositoryFile: "backupDst",
Password: "secretDst",
PasswordFile: "passwordFileDst",
},
FromRepo: true,
},
{
// Test if RepositoryFile and PasswordCommand are parsed correctly.
Opts: secondaryRepoOptions{
RepositoryFile: "backupDst",
PasswordCommand: "echo secretDst",
},
DstGOpts: GlobalOptions{
RepositoryFile: "backupDst",
Password: "secretDst",
PasswordCommand: "echo secretDst",
},
FromRepo: true,
},
{
// Test if LegacyRepo and Password are parsed correctly.
Opts: secondaryRepoOptions{
LegacyRepo: "backupDst",
password: "secretDst",
},
DstGOpts: GlobalOptions{
Repo: "backupDst",
Password: "secretDst",
},
},
{
// Test if LegacyRepositoryFile and LegacyPasswordFile are parsed correctly.
Opts: secondaryRepoOptions{
LegacyRepositoryFile: "backupDst",
LegacyPasswordFile: "passwordFileDst",
},
DstGOpts: GlobalOptions{
RepositoryFile: "backupDst",
Password: "secretDst",
PasswordFile: "passwordFileDst",
},
},
{
// Test if LegacyRepositoryFile and LegacyPasswordCommand are parsed correctly.
Opts: secondaryRepoOptions{
LegacyRepositoryFile: "backupDst",
LegacyPasswordCommand: "echo secretDst",
},
DstGOpts: GlobalOptions{
RepositoryFile: "backupDst",
Password: "secretDst",
PasswordCommand: "echo secretDst",
},
},
}
//invalidSecondaryRepoTestCases is a list with test cases that must fail
var invalidSecondaryRepoTestCases = []secondaryRepoTestCase{
{
// Test must fail on no repo given.
Opts: secondaryRepoOptions{},
},
{
// Test must fail as Repo and RepositoryFile are both given
Opts: secondaryRepoOptions{
Repo: "backupDst",
RepositoryFile: "backupDst",
},
},
{
// Test must fail as PasswordFile and PasswordCommand are both given
Opts: secondaryRepoOptions{
Repo: "backupDst",
PasswordFile: "passwordFileDst",
PasswordCommand: "notEmpty",
},
},
{
// Test must fail as PasswordFile does not exist
Opts: secondaryRepoOptions{
Repo: "backupDst",
PasswordFile: "NonExistingFile",
},
},
{
// Test must fail as PasswordCommand does not exist
Opts: secondaryRepoOptions{
Repo: "backupDst",
PasswordCommand: "notEmpty",
},
},
{
// Test must fail as current and legacy options are mixed
Opts: secondaryRepoOptions{
Repo: "backupDst",
LegacyRepo: "backupDst",
},
},
{
// Test must fail as current and legacy options are mixed
Opts: secondaryRepoOptions{
Repo: "backupDst",
LegacyPasswordCommand: "notEmpty",
},
},
}
//gOpts defines the Global options used in the secondary repository tests
var gOpts = GlobalOptions{
Repo: "backupSrc",
RepositoryFile: "backupSrc",
Password: "secretSrc",
PasswordFile: "passwordFileSrc",
}
//Create temp dir to create password file.
dir := rtest.TempDir(t)
cleanup := rtest.Chdir(t, dir)
defer cleanup()
//Create temporary password file
err := os.WriteFile(filepath.Join(dir, "passwordFileDst"), []byte("secretDst"), 0666)
rtest.OK(t, err)
// Test all valid cases
for _, testCase := range validSecondaryRepoTestCases {
DstGOpts, isFromRepo, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination")
rtest.OK(t, err)
rtest.Equals(t, DstGOpts, testCase.DstGOpts)
rtest.Equals(t, isFromRepo, testCase.FromRepo)
}
// Test all invalid cases
for _, testCase := range invalidSecondaryRepoTestCases {
_, _, err := fillSecondaryGlobalOpts(context.TODO(), testCase.Opts, gOpts, "destination")
rtest.Assert(t, err != nil, "Expected error, but function did not return an error")
}
}