Backport of NET 1594 - Snapshot Agent Filename Should Include Consul Version / Datacenter into release/1.16.x (#18638)
* backport of commit 5e1bae53412b563912a5c3042c6e48bffd09d75d * backport of commit 8805feb78ddc01a7b0a6ae0a3d16968ef446ac3d * backport of commit 99443a27650a39fd62dab512eb93aa4337ef0899 * backport of commit 37155cdc2ae636ad1a43e659649823a3db815cee * backport of commit c7971c9884fa870dd78ca8758fa2180e8d257b4b * backport of commit 2b66e28fc2e6c43ff5113931a74803ad98b9934e * backport of commit 2c9f48e0c2b5b4fa2bb2131374b8b32d4d9c5654 * backport of commit 0130674592727129bf247815d5b7236c693278e3 * backport of commit 171e0a5ba76a61530e33e7b5ce7c484f4884d05e * NET 1594 - Snapshot Agent Filename Should Include Consul Version / Datacenter (#18625) * init * tests added and few fixes * revert arg message * changelog added * removed var declaration * fix CI * fix test * added node name and status * updated save.mdx * added example * fix tense * fix description --------- Co-authored-by: absolutelightning <ashesh.vidyut@hashicorp.com> Co-authored-by: Ashesh Vidyut <134911583+absolutelightning@users.noreply.github.com>
This commit is contained in:
parent
c693ec3290
commit
7ce902c8d3
|
@ -0,0 +1,5 @@
|
||||||
|
```release-note:improvement
|
||||||
|
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.
|
||||||
|
```
|
|
@ -6,7 +6,10 @@ package save
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
"github.com/rboyer/safeio"
|
"github.com/rboyer/safeio"
|
||||||
|
@ -23,10 +26,18 @@ func New(ui cli.Ui) *cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
type cmd struct {
|
type cmd struct {
|
||||||
UI cli.Ui
|
UI cli.Ui
|
||||||
flags *flag.FlagSet
|
flags *flag.FlagSet
|
||||||
http *flags.HTTPFlags
|
http *flags.HTTPFlags
|
||||||
help string
|
help string
|
||||||
|
appendFileNameFlag flags.StringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmd) getAppendFileNameFlag() *flag.FlagSet {
|
||||||
|
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) init() {
|
func (c *cmd) init() {
|
||||||
|
@ -34,6 +45,7 @@ func (c *cmd) init() {
|
||||||
c.http = &flags.HTTPFlags{}
|
c.http = &flags.HTTPFlags{}
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
flags.Merge(c.flags, c.http.ServerFlags())
|
flags.Merge(c.flags, c.http.ServerFlags())
|
||||||
|
flags.Merge(c.flags, c.getAppendFileNameFlag())
|
||||||
c.help = flags.Usage(help, c.flags)
|
c.help = flags.Usage(help, c.flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +70,62 @@ func (c *cmd) Run(args []string) int {
|
||||||
|
|
||||||
// Create and test the HTTP client
|
// Create and test the HTTP client
|
||||||
client, err := c.http.APIClient()
|
client, err := c.http.APIClient()
|
||||||
|
|
||||||
|
appendFileNameFlags := strings.Split(c.appendFileNameFlag.String(), ",")
|
||||||
|
|
||||||
|
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))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
|
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -72,6 +72,58 @@ 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,node,status",
|
||||||
|
"-http-addr=" + a.HTTPAddr(),
|
||||||
|
file,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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) {
|
func TestSnapshotSaveCommand(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("too slow for testing.Short")
|
t.Skip("too slow for testing.Short")
|
||||||
|
|
|
@ -49,6 +49,10 @@ Usage: `consul snapshot save [options] FILE`
|
||||||
|
|
||||||
@include 'http_api_options_server.mdx'
|
@include 'http_api_options_server.mdx'
|
||||||
|
|
||||||
|
- `-append-filename=<value>` - 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
|
## Examples
|
||||||
|
|
||||||
To create a snapshot from the leader server and save it to "backup.snap":
|
To create a snapshot from the leader server and save it to "backup.snap":
|
||||||
|
@ -73,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 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
|
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
|
leader is available. To target a specific server for a snapshot, you can run
|
||||||
the `consul snapshot save` command on that specific server.
|
the `consul snapshot save` command on that specific server.
|
||||||
|
|
Loading…
Reference in New Issue