Skip to content

Commit

Permalink
Merge branch 'main' into NET-3181-consul-GH-Issue-15709-Allow-log-fil…
Browse files Browse the repository at this point in the history
…e-naming-like-Nomad
  • Loading branch information
absolutelightning authored Sep 1, 2023
2 parents 2e3332a + 78e3cbe commit 0aa4f72
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changelog/18625.txt
Original file line number Diff line number Diff line change
@@ -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.
```
76 changes: 72 additions & 4 deletions command/snapshot/save/snapshot_save.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ package save
import (
"flag"
"fmt"
"golang.org/x/exp/slices"
"os"
"path/filepath"
"strings"

"github.com/mitchellh/cli"
"github.com/rboyer/safeio"
Expand All @@ -23,17 +26,26 @@ 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
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() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
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)
}

Expand All @@ -58,6 +70,62 @@ func (c *cmd) Run(args []string) int {

// Create and test the HTTP client
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 {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
Expand Down
52 changes: 52 additions & 0 deletions command/snapshot/save/snapshot_save_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
if testing.Short() {
t.Skip("too slow for testing.Short")
Expand Down
4 changes: 3 additions & 1 deletion internal/catalog/internal/types/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ func validatePortName(name string) error {

func validateProtocol(protocol pbcatalog.Protocol) error {
switch protocol {
case pbcatalog.Protocol_PROTOCOL_TCP,
case pbcatalog.Protocol_PROTOCOL_UNSPECIFIED,
// means pbcatalog.FailoverMode_FAILOVER_MODE_TCP
pbcatalog.Protocol_PROTOCOL_TCP,
pbcatalog.Protocol_PROTOCOL_HTTP,
pbcatalog.Protocol_PROTOCOL_HTTP2,
pbcatalog.Protocol_PROTOCOL_GRPC,
Expand Down
20 changes: 20 additions & 0 deletions internal/catalog/internal/types/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,26 @@ func TestValidatePortName(t *testing.T) {
})
}

func TestValidateProtocol(t *testing.T) {
// this test simply verifies that we accept all enum values specified in our proto
// in order to avoid validator drift.
for name, value := range pbcatalog.Protocol_value {
t.Run(name, func(t *testing.T) {
require.NoError(t, validateProtocol(pbcatalog.Protocol(value)))
})
}
}

func TestValidateHealth(t *testing.T) {
// this test simply verifies that we accept all enum values specified in our proto
// in order to avoid validator drift.
for name, value := range pbcatalog.Health_value {
t.Run(name, func(t *testing.T) {
require.NoError(t, validateHealth(pbcatalog.Health(value)))
})
}
}

func TestValidateWorkloadAddress(t *testing.T) {
type testCase struct {
addr *pbcatalog.WorkloadAddress
Expand Down
15 changes: 15 additions & 0 deletions website/content/commands/snapshot/save.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ Usage: `consul snapshot save [options] FILE`

@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

To create a snapshot from the leader server and save it to "backup.snap":
Expand All @@ -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
leader is available. To target a specific server for a snapshot, you can run
the `consul snapshot save` command on that specific server.
Expand Down
7 changes: 6 additions & 1 deletion website/content/docs/enterprise/admin-partitions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ Only resources in the `default` admin partition will be replicated to secondary

### DNS Queries

Client agents will be configured to operate within a specific admin partition. The DNS interface will only return results for the admin partition within the scope of the client.
When queried, the DNS interface returns results for a single admin partition.
The query may explicitly specify the admin partition to use in the lookup.
If you do not specify an admin partition in the query,
the lookup uses the admin partition of the Consul agent that received the query.
Server agents always exist within the `default` admin partition.
Client agents are configured to operate within a specific admin partition.

### Service Mesh Configurations

Expand Down

0 comments on commit 0aa4f72

Please sign in to comment.