From 871ea1eaf3dcd8da4641401425a10953d8fbcd27 Mon Sep 17 00:00:00 2001 From: Altan Orhon Date: Mon, 18 Mar 2024 13:20:45 -0700 Subject: [PATCH 1/2] Add support for specifying --host via environment variable This commit adds support for specifying the `--host` option via the `RESTIC_HOST` environment variable. This is done by extending option processing in `cmd_backup.go` and for `restic.SnapshotFilter` in `find.go`. --- changelog/unreleased/issue-4733 | 9 +++++++++ cmd/restic/cmd_backup.go | 7 ++++++- cmd/restic/find.go | 19 +++++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 changelog/unreleased/issue-4733 diff --git a/changelog/unreleased/issue-4733 b/changelog/unreleased/issue-4733 new file mode 100644 index 000000000..250c2ba68 --- /dev/null +++ b/changelog/unreleased/issue-4733 @@ -0,0 +1,9 @@ +Enhancement: Allow specifying `--host` via environment variable + +Restic commands that operate on snapshots, such as `restic backup` and +`restic snapshots`, support the `--host` flag to specify the hostname for +grouoping snapshots. They now permit selecting the hostname via the +environment variable `RESTIC_HOST`. `--host` still takes precedence over the +environment variable. + +https://github.com/restic/restic/issues/4733 \ No newline at end of file diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 8b2f1f808..d3e5a8546 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -114,7 +114,7 @@ func init() { f.BoolVar(&backupOptions.StdinCommand, "stdin-from-command", false, "interpret arguments as command to execute and store its stdout") f.Var(&backupOptions.Tags, "tag", "add `tags` for the new snapshot in the format `tag[,tag,...]` (can be specified multiple times)") f.UintVar(&backupOptions.ReadConcurrency, "read-concurrency", 0, "read `n` files concurrently (default: $RESTIC_READ_CONCURRENCY or 2)") - f.StringVarP(&backupOptions.Host, "host", "H", "", "set the `hostname` for the snapshot manually. To prevent an expensive rescan use the \"parent\" flag") + f.StringVarP(&backupOptions.Host, "host", "H", "", "set the `hostname` for the snapshot manually (default: $RESTIC_HOST). To prevent an expensive rescan use the \"parent\" flag") f.StringVar(&backupOptions.Host, "hostname", "", "set the `hostname` for the snapshot manually") err := f.MarkDeprecated("hostname", "use --host") if err != nil { @@ -137,6 +137,11 @@ func init() { // parse read concurrency from env, on error the default value will be used readConcurrency, _ := strconv.ParseUint(os.Getenv("RESTIC_READ_CONCURRENCY"), 10, 32) backupOptions.ReadConcurrency = uint(readConcurrency) + + // parse host from env, if not exists or empty the default value will be used + if host := os.Getenv("RESTIC_HOST"); host != "" { + backupOptions.Host = host + } } // filterExisting returns a slice of all existing items, or an error if no diff --git a/cmd/restic/find.go b/cmd/restic/find.go index a990b458d..7c28b3be5 100644 --- a/cmd/restic/find.go +++ b/cmd/restic/find.go @@ -2,6 +2,7 @@ package main import ( "context" + "os" "github.com/restic/restic/internal/restic" "github.com/spf13/pflag" @@ -14,17 +15,31 @@ func initMultiSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter, if !addHostShorthand { hostShorthand = "" } - flags.StringArrayVarP(&filt.Hosts, "host", hostShorthand, nil, "only consider snapshots for this `host` (can be specified multiple times)") + flags.StringArrayVarP(&filt.Hosts, "host", hostShorthand, nil, "only consider snapshots for this `host` (can be specified multiple times) (default: $RESTIC_HOST)") flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]` (can be specified multiple times)") flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path` (can be specified multiple times)") + + if len(filt.Hosts) == 0 { + // parse host from env, if not exists or empty the default value will be used + if host := os.Getenv("RESTIC_HOST"); host != "" { + filt.Hosts = []string{host} + } + } } // initSingleSnapshotFilter is used for commands that work on a single snapshot // MUST be combined with restic.FindFilteredSnapshot func initSingleSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter) { - flags.StringArrayVarP(&filt.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when snapshot ID \"latest\" is given (can be specified multiple times)") + flags.StringArrayVarP(&filt.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when snapshot ID \"latest\" is given (can be specified multiple times) (default: $RESTIC_HOST)") flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]`, when snapshot ID \"latest\" is given (can be specified multiple times)") flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path`, when snapshot ID \"latest\" is given (can be specified multiple times)") + + if len(filt.Hosts) == 0 { + // parse host from env, if not exists or empty the default value will be used + if host := os.Getenv("RESTIC_HOST"); host != "" { + filt.Hosts = []string{host} + } + } } // FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots. From 347e9d07657f358c297a21aabb6224c38502a08f Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Wed, 24 Apr 2024 21:49:34 +0200 Subject: [PATCH 2/2] complete RESITC_HOST environment handling & test --- changelog/unreleased/issue-4733 | 3 +- cmd/restic/find.go | 16 ++++----- cmd/restic/find_test.go | 61 +++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 cmd/restic/find_test.go diff --git a/changelog/unreleased/issue-4733 b/changelog/unreleased/issue-4733 index 250c2ba68..1fc271587 100644 --- a/changelog/unreleased/issue-4733 +++ b/changelog/unreleased/issue-4733 @@ -6,4 +6,5 @@ grouoping snapshots. They now permit selecting the hostname via the environment variable `RESTIC_HOST`. `--host` still takes precedence over the environment variable. -https://github.com/restic/restic/issues/4733 \ No newline at end of file +https://github.com/restic/restic/issues/4733 +https://github.com/restic/restic/pull/4734 diff --git a/cmd/restic/find.go b/cmd/restic/find.go index 7c28b3be5..c7754d5d9 100644 --- a/cmd/restic/find.go +++ b/cmd/restic/find.go @@ -19,11 +19,9 @@ func initMultiSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter, flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]` (can be specified multiple times)") flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path` (can be specified multiple times)") - if len(filt.Hosts) == 0 { - // parse host from env, if not exists or empty the default value will be used - if host := os.Getenv("RESTIC_HOST"); host != "" { - filt.Hosts = []string{host} - } + // set default based on env if set + if host := os.Getenv("RESTIC_HOST"); host != "" { + filt.Hosts = []string{host} } } @@ -34,11 +32,9 @@ func initSingleSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter) flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]`, when snapshot ID \"latest\" is given (can be specified multiple times)") flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path`, when snapshot ID \"latest\" is given (can be specified multiple times)") - if len(filt.Hosts) == 0 { - // parse host from env, if not exists or empty the default value will be used - if host := os.Getenv("RESTIC_HOST"); host != "" { - filt.Hosts = []string{host} - } + // set default based on env if set + if host := os.Getenv("RESTIC_HOST"); host != "" { + filt.Hosts = []string{host} } } diff --git a/cmd/restic/find_test.go b/cmd/restic/find_test.go new file mode 100644 index 000000000..a98a14f04 --- /dev/null +++ b/cmd/restic/find_test.go @@ -0,0 +1,61 @@ +package main + +import ( + "testing" + + "github.com/restic/restic/internal/restic" + rtest "github.com/restic/restic/internal/test" + "github.com/spf13/pflag" +) + +func TestSnapshotFilter(t *testing.T) { + for _, test := range []struct { + name string + args []string + expected []string + env string + }{ + { + "no value", + []string{}, + nil, + "", + }, + { + "args only", + []string{"--host", "abc"}, + []string{"abc"}, + "", + }, + { + "env default", + []string{}, + []string{"def"}, + "def", + }, + { + "both", + []string{"--host", "abc"}, + []string{"abc"}, + "def", + }, + } { + t.Run(test.name, func(t *testing.T) { + t.Setenv("RESTIC_HOST", test.env) + + for _, mode := range []bool{false, true} { + set := pflag.NewFlagSet("test", pflag.PanicOnError) + flt := &restic.SnapshotFilter{} + if mode { + initMultiSnapshotFilter(set, flt, false) + } else { + initSingleSnapshotFilter(set, flt) + } + err := set.Parse(test.args) + rtest.OK(t, err) + + rtest.Equals(t, test.expected, flt.Hosts, "unexpected hosts") + } + }) + } +}