From 5e1bae53412b563912a5c3042c6e48bffd09d75d Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 14:48:54 +0530 Subject: [PATCH 01/12] init --- command/snapshot/save/snapshot_save.go | 42 +++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/command/snapshot/save/snapshot_save.go b/command/snapshot/save/snapshot_save.go index 2395a4f3a038..16e473b1f016 100644 --- a/command/snapshot/save/snapshot_save.go +++ b/command/snapshot/save/snapshot_save.go @@ -6,7 +6,10 @@ package save import ( "flag" "fmt" + "github.com/hashicorp/consul/version" + "golang.org/x/exp/slices" "os" + "strings" "github.com/mitchellh/cli" "github.com/rboyer/safeio" @@ -23,10 +26,18 @@ func New(ui cli.Ui) *cmd { } type cmd struct { - UI cli.Ui - flags *flag.FlagSet - http *flags.HTTPFlags - help string + UI cli.Ui + flags *flag.FlagSet + http *flags.HTTPFlags + help string + appendFileName flags.StringValue +} + +func (c *cmd) getAppendFileNameFlag() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.Var(&c.appendFileName, "append-filename", "Append filename flag takes two possible values. "+ + "1. version, 2. dc. It appends consul version and datacenter to filename given in command") + return fs } func (c *cmd) init() { @@ -34,6 +45,7 @@ func (c *cmd) init() { c.http = &flags.HTTPFlags{} flags.Merge(c.flags, c.http.ClientFlags()) flags.Merge(c.flags, c.http.ServerFlags()) + flags.Merge(c.flags, c.getAppendFileNameFlag()) c.help = flags.Usage(help, c.flags) } @@ -52,12 +64,32 @@ func (c *cmd) Run(args []string) int { case 1: file = args[0] default: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 3, got %d)", len(args))) return 1 } // Create and test the HTTP client client, err := c.http.APIClient() + + appendFileNameFlags := strings.Split(c.appendFileName.String(), ",") + + if slices.Contains(appendFileNameFlags, "version") { + file = file + "-" + version.GetHumanVersion() + } + + if slices.Contains(appendFileNameFlags, "dc") { + agentSelfResponse, err := client.Agent().Self() + if err != nil { + c.UI.Error(fmt.Sprintf("Error connecting to Consul agent and fetching datacenter: %s", err)) + return 1 + } + if config, ok := agentSelfResponse["Config"]; ok { + if datacenter, ok := config["Datacenter"]; ok { + file = file + "-" + datacenter.(string) + } + } + } + if err != nil { c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) return 1 From 8805feb78ddc01a7b0a6ae0a3d16968ef446ac3d Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 15:15:00 +0530 Subject: [PATCH 02/12] tests added and few fixes --- command/snapshot/save/snapshot_save.go | 49 ++++++++++++++------- command/snapshot/save/snapshot_save_test.go | 42 ++++++++++++++++++ 2 files changed, 74 insertions(+), 17 deletions(-) diff --git a/command/snapshot/save/snapshot_save.go b/command/snapshot/save/snapshot_save.go index 16e473b1f016..0f4e6d7e984a 100644 --- a/command/snapshot/save/snapshot_save.go +++ b/command/snapshot/save/snapshot_save.go @@ -6,9 +6,9 @@ package save import ( "flag" "fmt" - "github.com/hashicorp/consul/version" "golang.org/x/exp/slices" "os" + "path/filepath" "strings" "github.com/mitchellh/cli" @@ -26,16 +26,16 @@ func New(ui cli.Ui) *cmd { } type cmd struct { - UI cli.Ui - flags *flag.FlagSet - http *flags.HTTPFlags - help string - appendFileName flags.StringValue + UI cli.Ui + flags *flag.FlagSet + http *flags.HTTPFlags + help string + appendFileNameFlag flags.StringValue } func (c *cmd) getAppendFileNameFlag() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) - fs.Var(&c.appendFileName, "append-filename", "Append filename flag takes two possible values. "+ + fs.Var(&c.appendFileNameFlag, "append-filename", "Append filename flag takes two possible values. "+ "1. version, 2. dc. It appends consul version and datacenter to filename given in command") return fs } @@ -71,23 +71,38 @@ func (c *cmd) Run(args []string) int { // Create and test the HTTP client client, err := c.http.APIClient() - appendFileNameFlags := strings.Split(c.appendFileName.String(), ",") + appendFileNameFlags := strings.Split(c.appendFileNameFlag.String(), ",") - if slices.Contains(appendFileNameFlags, "version") { - file = file + "-" + version.GetHumanVersion() - } + var agentSelfResponse map[string]map[string]interface{} - if slices.Contains(appendFileNameFlags, "dc") { - agentSelfResponse, err := client.Agent().Self() + if len(appendFileNameFlags) != 0 { + agentSelfResponse, err = client.Agent().Self() if err != nil { - c.UI.Error(fmt.Sprintf("Error connecting to Consul agent and fetching datacenter: %s", err)) + c.UI.Error(fmt.Sprintf("Error connecting to Consul agent and fetching datacenter/version: %s", err)) return 1 } - if config, ok := agentSelfResponse["Config"]; ok { - if datacenter, ok := config["Datacenter"]; ok { - file = file + "-" + datacenter.(string) + + fileExt := filepath.Ext(file) + fileNameWithoutExt := strings.TrimSuffix(file, fileExt) + + if slices.Contains(appendFileNameFlags, "version") { + if config, ok := agentSelfResponse["Config"]; ok { + if version, ok := config["Version"]; ok { + fileNameWithoutExt = fileNameWithoutExt + "-" + version.(string) + } } } + + if slices.Contains(appendFileNameFlags, "dc") { + if config, ok := agentSelfResponse["Config"]; ok { + if datacenter, ok := config["Datacenter"]; ok { + fileNameWithoutExt = fileNameWithoutExt + "-" + datacenter.(string) + } + } + } + + //adding extension back + file = fileNameWithoutExt + fileExt } if err != nil { diff --git a/command/snapshot/save/snapshot_save_test.go b/command/snapshot/save/snapshot_save_test.go index a95b537e9475..47be3c77363b 100644 --- a/command/snapshot/save/snapshot_save_test.go +++ b/command/snapshot/save/snapshot_save_test.go @@ -72,6 +72,48 @@ func TestSnapshotSaveCommand_Validation(t *testing.T) { } } +func TestSnapshotSaveCommandWithAppendFileNameFlag(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + a := agent.NewTestAgent(t, ``) + defer a.Shutdown() + client := a.Client() + + ui := cli.NewMockUi() + c := New(ui) + + dir := testutil.TempDir(t, "snapshot") + file := filepath.Join(dir, "backup.tgz") + args := []string{ + "-append-filename=version,dc", + file, + } + + newFilePath := filepath.Join(dir, "backup"+"-"+a.Config.Version+"-"+a.Config.Datacenter+".tgz") + + code := c.Run(args) + if code != 0 { + t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) + } + + fi, err := os.Stat(newFilePath) + require.NoError(t, err) + require.Equal(t, fi.Mode(), os.FileMode(0600)) + + f, err := os.Open(newFilePath) + if err != nil { + t.Fatalf("err: %v", err) + } + defer f.Close() + + if err := client.Snapshot().Restore(nil, f); err != nil { + t.Fatalf("err: %v", err) + } +} + func TestSnapshotSaveCommand(t *testing.T) { if testing.Short() { t.Skip("too slow for testing.Short") From 99443a27650a39fd62dab512eb93aa4337ef0899 Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 15:15:53 +0530 Subject: [PATCH 03/12] revert arg message --- command/snapshot/save/snapshot_save.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/snapshot/save/snapshot_save.go b/command/snapshot/save/snapshot_save.go index 0f4e6d7e984a..fb006eef5b18 100644 --- a/command/snapshot/save/snapshot_save.go +++ b/command/snapshot/save/snapshot_save.go @@ -64,7 +64,7 @@ func (c *cmd) Run(args []string) int { case 1: file = args[0] default: - c.UI.Error(fmt.Sprintf("Too many arguments (expected 1 or 3, got %d)", len(args))) + c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args))) return 1 } From 37155cdc2ae636ad1a43e659649823a3db815cee Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 15:25:58 +0530 Subject: [PATCH 04/12] changelog added --- .changelog/18625.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .changelog/18625.txt diff --git a/.changelog/18625.txt b/.changelog/18625.txt new file mode 100644 index 000000000000..763b47c1eb3a --- /dev/null +++ b/.changelog/18625.txt @@ -0,0 +1,4 @@ +```release-note:improvement +Adds flag -append-filename (which works on two values version and dc) to consul snapshot save command. +Adding the flag -append-filename version,dc will add consul version and consul datacenter in the file name given in the snapshot save command before the file extension. +``` From c7971c9884fa870dd78ca8758fa2180e8d257b4b Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 15:47:22 +0530 Subject: [PATCH 05/12] removed var declaration --- command/snapshot/save/snapshot_save.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/command/snapshot/save/snapshot_save.go b/command/snapshot/save/snapshot_save.go index fb006eef5b18..5ac9427c7c72 100644 --- a/command/snapshot/save/snapshot_save.go +++ b/command/snapshot/save/snapshot_save.go @@ -73,10 +73,8 @@ func (c *cmd) Run(args []string) int { appendFileNameFlags := strings.Split(c.appendFileNameFlag.String(), ",") - var agentSelfResponse map[string]map[string]interface{} - if len(appendFileNameFlags) != 0 { - agentSelfResponse, err = client.Agent().Self() + agentSelfResponse, err := client.Agent().Self() if err != nil { c.UI.Error(fmt.Sprintf("Error connecting to Consul agent and fetching datacenter/version: %s", err)) return 1 From 2b66e28fc2e6c43ff5113931a74803ad98b9934e Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 16:42:27 +0530 Subject: [PATCH 06/12] fix CI --- command/snapshot/save/snapshot_save.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/snapshot/save/snapshot_save.go b/command/snapshot/save/snapshot_save.go index 5ac9427c7c72..99350a947b3f 100644 --- a/command/snapshot/save/snapshot_save.go +++ b/command/snapshot/save/snapshot_save.go @@ -73,7 +73,7 @@ func (c *cmd) Run(args []string) int { appendFileNameFlags := strings.Split(c.appendFileNameFlag.String(), ",") - if len(appendFileNameFlags) != 0 { + if len(appendFileNameFlags) != 0 && len(c.appendFileNameFlag.String()) > 0 { agentSelfResponse, err := client.Agent().Self() if err != nil { c.UI.Error(fmt.Sprintf("Error connecting to Consul agent and fetching datacenter/version: %s", err)) From 2c9f48e0c2b5b4fa2bb2131374b8b32d4d9c5654 Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 17:03:40 +0530 Subject: [PATCH 07/12] fix test --- command/snapshot/save/snapshot_save_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/command/snapshot/save/snapshot_save_test.go b/command/snapshot/save/snapshot_save_test.go index 47be3c77363b..c7d7367e418d 100644 --- a/command/snapshot/save/snapshot_save_test.go +++ b/command/snapshot/save/snapshot_save_test.go @@ -89,6 +89,7 @@ func TestSnapshotSaveCommandWithAppendFileNameFlag(t *testing.T) { file := filepath.Join(dir, "backup.tgz") args := []string{ "-append-filename=version,dc", + "-http-addr=" + a.HTTPAddr(), file, } From 0130674592727129bf247815d5b7236c693278e3 Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 22:02:32 +0530 Subject: [PATCH 08/12] added node name and status --- .changelog/18625.txt | 5 +++-- command/snapshot/save/snapshot_save.go | 25 ++++++++++++++++++++- command/snapshot/save/snapshot_save_test.go | 13 +++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/.changelog/18625.txt b/.changelog/18625.txt index 763b47c1eb3a..8474cac8dc1d 100644 --- a/.changelog/18625.txt +++ b/.changelog/18625.txt @@ -1,4 +1,5 @@ ```release-note:improvement -Adds flag -append-filename (which works on two values version and dc) to consul snapshot save command. -Adding the flag -append-filename version,dc will add consul version and consul datacenter in the file name given in the snapshot save command before the file extension. +Adds flag -append-filename (which works on values version, dc, node and status) to consul snapshot save command. +Adding the flag -append-filename version,dc,node,status will add consul version, consul datacenter, node name and leader/follower +(status) in the file name given in the snapshot save command before the file extension. ``` diff --git a/command/snapshot/save/snapshot_save.go b/command/snapshot/save/snapshot_save.go index 99350a947b3f..c9ed61b281f8 100644 --- a/command/snapshot/save/snapshot_save.go +++ b/command/snapshot/save/snapshot_save.go @@ -36,7 +36,7 @@ type cmd struct { func (c *cmd) getAppendFileNameFlag() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) fs.Var(&c.appendFileNameFlag, "append-filename", "Append filename flag takes two possible values. "+ - "1. version, 2. dc. It appends consul version and datacenter to filename given in command") + "1. version, 2. dc. 3. node 5. status. It appends consul version and datacenter to filename given in command") return fs } @@ -99,6 +99,29 @@ func (c *cmd) Run(args []string) int { } } + if slices.Contains(appendFileNameFlags, "node") { + if config, ok := agentSelfResponse["Config"]; ok { + if nodeName, ok := config["NodeName"]; ok { + fileNameWithoutExt = fileNameWithoutExt + "-" + nodeName.(string) + } + } + } + + if slices.Contains(appendFileNameFlags, "status") { + if status, ok := agentSelfResponse["Stats"]; ok { + if config, ok := status["consul"]; ok { + configMap := config.(map[string]interface{}) + if leader, ok := configMap["leader"]; ok { + if leader == "true" { + fileNameWithoutExt = fileNameWithoutExt + "-" + "leader" + } else { + fileNameWithoutExt = fileNameWithoutExt + "-" + "follower" + } + } + } + } + } + //adding extension back file = fileNameWithoutExt + fileExt } diff --git a/command/snapshot/save/snapshot_save_test.go b/command/snapshot/save/snapshot_save_test.go index c7d7367e418d..8e339cd51f5b 100644 --- a/command/snapshot/save/snapshot_save_test.go +++ b/command/snapshot/save/snapshot_save_test.go @@ -88,12 +88,21 @@ func TestSnapshotSaveCommandWithAppendFileNameFlag(t *testing.T) { dir := testutil.TempDir(t, "snapshot") file := filepath.Join(dir, "backup.tgz") args := []string{ - "-append-filename=version,dc", + "-append-filename=version,dc,node,status", "-http-addr=" + a.HTTPAddr(), file, } - newFilePath := filepath.Join(dir, "backup"+"-"+a.Config.Version+"-"+a.Config.Datacenter+".tgz") + stats := a.Stats() + + status := "follower" + + if stats["consul"]["leader"] == "true" { + status = "leader" + } + + newFilePath := filepath.Join(dir, "backup"+"-"+a.Config.Version+"-"+a.Config.Datacenter+ + "-"+a.Config.NodeName+"-"+status+".tgz") code := c.Run(args) if code != 0 { From 171e0a5ba76a61530e33e7b5ce7c484f4884d05e Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 22:09:59 +0530 Subject: [PATCH 09/12] updated save.mdx --- website/content/commands/snapshot/save.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/content/commands/snapshot/save.mdx b/website/content/commands/snapshot/save.mdx index 18ffc50150ab..3059da35db27 100644 --- a/website/content/commands/snapshot/save.mdx +++ b/website/content/commands/snapshot/save.mdx @@ -49,6 +49,10 @@ Usage: `consul snapshot save [options] FILE` @include 'http_api_options_server.mdx' +- `-append-filename=` - Value can be - version,dc,node,status +Adds consul version, datacenter name, node name, and status (leader/follower) +to the file name before the extension separated by `-` + ## Examples To create a snapshot from the leader server and save it to "backup.snap": From a581843671e539922d4f32358c209a5c5a26ee6c Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 22:23:51 +0530 Subject: [PATCH 10/12] added example --- website/content/commands/snapshot/save.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/website/content/commands/snapshot/save.mdx b/website/content/commands/snapshot/save.mdx index 3059da35db27..ec8e425e2dd6 100644 --- a/website/content/commands/snapshot/save.mdx +++ b/website/content/commands/snapshot/save.mdx @@ -77,6 +77,17 @@ $ consul snapshot save -stale backup.snap # ... ``` +To create snapshot file with consul version, datacenter, node name and leader/follower info, +run + +```shell-session +$ consul snapshot save -append-filename node,status,version,dc backup.snap +#... +``` + +File name create will be like backup-%CONSUL_VERSION%-%DC_NAME%-%NODE_NAME%-%STATUS.snap +example - backup-1.17.0-dc1-local-machine-leader.tgz + This is useful for situations where a cluster is in a degraded state and no leader is available. To target a specific server for a snapshot, you can run the `consul snapshot save` command on that specific server. From 1d23f774b9bcddb8ef7c6f82255aecf6303ab9c0 Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Thu, 31 Aug 2023 22:25:24 +0530 Subject: [PATCH 11/12] fix tense --- website/content/commands/snapshot/save.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/content/commands/snapshot/save.mdx b/website/content/commands/snapshot/save.mdx index ec8e425e2dd6..dfd4d4086d45 100644 --- a/website/content/commands/snapshot/save.mdx +++ b/website/content/commands/snapshot/save.mdx @@ -85,7 +85,7 @@ $ consul snapshot save -append-filename node,status,version,dc backup.snap #... ``` -File name create will be like backup-%CONSUL_VERSION%-%DC_NAME%-%NODE_NAME%-%STATUS.snap +File name created will be like backup-%CONSUL_VERSION%-%DC_NAME%-%NODE_NAME%-%STATUS.snap example - backup-1.17.0-dc1-local-machine-leader.tgz This is useful for situations where a cluster is in a degraded state and no From 45c709c619412f7de7371159c4e0922b7d4ea717 Mon Sep 17 00:00:00 2001 From: absolutelightning Date: Fri, 1 Sep 2023 09:54:05 +0530 Subject: [PATCH 12/12] fix description --- command/snapshot/save/snapshot_save.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/snapshot/save/snapshot_save.go b/command/snapshot/save/snapshot_save.go index c9ed61b281f8..c774e6d89e1d 100644 --- a/command/snapshot/save/snapshot_save.go +++ b/command/snapshot/save/snapshot_save.go @@ -35,8 +35,8 @@ type cmd struct { func (c *cmd) getAppendFileNameFlag() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) - fs.Var(&c.appendFileNameFlag, "append-filename", "Append filename flag takes two possible values. "+ - "1. version, 2. dc. 3. node 5. status. It appends consul version and datacenter to filename given in command") + fs.Var(&c.appendFileNameFlag, "append-filename", "Append filename flag supports the following "+ + "comma-separated arguments. 1. version, 2. dc. 3. node 4. status. It appends these values to the filename provided in the command") return fs }