cli: Add `json` and `-t` flags to `server members` command (#16444)
* cli: Add and flags to server members * Update website/content/docs/commands/server/members.mdx Co-authored-by: James Rasell <jrasell@users.noreply.github.com> * Update website/content/docs/commands/server/members.mdx Co-authored-by: James Rasell <jrasell@users.noreply.github.com> * cli: update the server memebers tests to use must * cli: add flags addition to changelog --------- Co-authored-by: James Rasell <jrasell@users.noreply.github.com>
This commit is contained in:
parent
e4e53872be
commit
eeb3766575
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
cli: Added `-json` and `-t` flag to `server members` command
|
||||
```
|
|
@ -35,6 +35,12 @@ Server Members Options:
|
|||
-verbose
|
||||
Show detailed information about each member. This dumps a raw set of tags
|
||||
which shows more information than the default output format.
|
||||
|
||||
-json
|
||||
Output the latest information about each member in a JSON format.
|
||||
|
||||
-t
|
||||
Format and display latest information about each member using a Go template.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
@ -43,6 +49,9 @@ func (c *ServerMembersCommand) AutocompleteFlags() complete.Flags {
|
|||
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
|
||||
complete.Flags{
|
||||
"-detailed": complete.PredictNothing,
|
||||
"-verbose": complete.PredictNothing,
|
||||
"-json": complete.PredictNothing,
|
||||
"-t": complete.PredictAnything,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -57,12 +66,15 @@ func (c *ServerMembersCommand) Synopsis() string {
|
|||
func (c *ServerMembersCommand) Name() string { return "server members" }
|
||||
|
||||
func (c *ServerMembersCommand) Run(args []string) int {
|
||||
var detailed, verbose bool
|
||||
var detailed, verbose, json bool
|
||||
var tmpl string
|
||||
|
||||
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
flags.BoolVar(&detailed, "detailed", false, "Show detailed output")
|
||||
flags.BoolVar(&verbose, "verbose", false, "Show detailed output")
|
||||
flags.BoolVar(&json, "json", false, "")
|
||||
flags.StringVar(&tmpl, "t", "", "")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
|
@ -103,6 +115,17 @@ func (c *ServerMembersCommand) Run(args []string) int {
|
|||
// Sort the members
|
||||
sort.Sort(api.AgentMembersNameSort(srvMembers.Members))
|
||||
|
||||
if json || len(tmpl) > 0 {
|
||||
out, err := Format(json, tmpl, srvMembers.Members)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(out)
|
||||
return 0
|
||||
}
|
||||
|
||||
// Determine the leaders per region.
|
||||
leaders, leaderErr := regionLeaders(client, srvMembers.Members)
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/ci"
|
||||
"github.com/hashicorp/nomad/command/agent"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/shoenig/test/must"
|
||||
)
|
||||
|
||||
func TestServerMembersCommand_Implements(t *testing.T) {
|
||||
|
@ -25,30 +28,46 @@ func TestServerMembersCommand_Run(t *testing.T) {
|
|||
|
||||
// Get our own node name
|
||||
name, err := client.Agent().NodeName()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
must.NoError(t, err)
|
||||
|
||||
// Query the members
|
||||
if code := cmd.Run([]string{"-address=" + url}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
code := cmd.Run([]string{"-address=" + url})
|
||||
|
||||
if out := ui.OutputWriter.String(); !strings.Contains(out, name) {
|
||||
t.Fatalf("expected %q in output, got: %s", name, out)
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// Query members with verbose output
|
||||
if code := cmd.Run([]string{"-address=" + url, "-verbose"}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
code = cmd.Run([]string{"-address=" + url, "-verbose"})
|
||||
must.Zero(t, code)
|
||||
|
||||
// Still support previous detailed flag
|
||||
if code := cmd.Run([]string{"-address=" + url, "-detailed"}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
if out := ui.OutputWriter.String(); !strings.Contains(out, "Tags") {
|
||||
t.Fatalf("expected tags in output, got: %s", out)
|
||||
}
|
||||
code = cmd.Run([]string{"-address=" + url, "-detailed"})
|
||||
must.Zero(t, code)
|
||||
|
||||
must.StrContains(t, ui.OutputWriter.String(), "Tags")
|
||||
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// List json
|
||||
code = cmd.Run([]string{"-address=" + url, "-json"})
|
||||
must.Zero(t, code)
|
||||
|
||||
outJson := []api.AgentMember{}
|
||||
err = json.Unmarshal(ui.OutputWriter.Bytes(), &outJson)
|
||||
must.NoError(t, err)
|
||||
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// Go template to format the output
|
||||
code = cmd.Run([]string{"-address=" + url, "-t", "{{range .}}{{ .Status }}{{end}}"})
|
||||
must.Zero(t, code)
|
||||
|
||||
out := ui.OutputWriter.String()
|
||||
must.StrContains(t, out, "alive")
|
||||
|
||||
ui.ErrorWriter.Reset()
|
||||
}
|
||||
|
||||
func TestMembersCommand_Fails(t *testing.T) {
|
||||
|
@ -57,21 +76,17 @@ func TestMembersCommand_Fails(t *testing.T) {
|
|||
cmd := &ServerMembersCommand{Meta: Meta{Ui: ui}}
|
||||
|
||||
// Fails on misuse
|
||||
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
|
||||
t.Fatalf("expected exit code 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
|
||||
t.Fatalf("expected help output, got: %s", out)
|
||||
}
|
||||
code := cmd.Run([]string{"some", "bad", "args"})
|
||||
must.One(t, code)
|
||||
|
||||
must.StrContains(t, ui.ErrorWriter.String(), commandErrorText(cmd))
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
// Fails on connection failure
|
||||
if code := cmd.Run([]string{"-address=nope"}); code != 1 {
|
||||
t.Fatalf("expected exit code 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying servers") {
|
||||
t.Fatalf("expected failed query error, got: %s", out)
|
||||
}
|
||||
code = cmd.Run([]string{"-address=nope"})
|
||||
must.One(t, code)
|
||||
|
||||
must.StrContains(t, ui.ErrorWriter.String(), "Error querying servers")
|
||||
}
|
||||
|
||||
// Tests that a single server region that left should still
|
||||
|
@ -99,35 +114,29 @@ func TestServerMembersCommand_MultiRegion_Leave(t *testing.T) {
|
|||
addr := fmt.Sprintf("127.0.0.1:%d",
|
||||
srv1.Agent.Server().GetConfig().SerfConfig.MemberlistConfig.BindPort)
|
||||
|
||||
if _, err := srv2.Agent.Server().Join([]string{addr}); err != nil {
|
||||
t.Fatalf("Join err: %v", err)
|
||||
}
|
||||
_, err := srv2.Agent.Server().Join([]string{addr})
|
||||
must.NoError(t, err)
|
||||
|
||||
ui := cli.NewMockUi()
|
||||
cmd := &ServerMembersCommand{Meta: Meta{Ui: ui}}
|
||||
|
||||
// Get our own node name
|
||||
name, err := client1.Agent().NodeName()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
must.NoError(t, err)
|
||||
|
||||
// Query the members
|
||||
if code := cmd.Run([]string{"-address=" + url}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
if out := ui.OutputWriter.String(); !strings.Contains(out, name) {
|
||||
t.Fatalf("expected %q in output, got: %s", name, out)
|
||||
}
|
||||
code := cmd.Run([]string{"-address=" + url})
|
||||
must.Zero(t, code)
|
||||
|
||||
must.StrContains(t, ui.OutputWriter.String(), name)
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// Make one of the servers leave
|
||||
srv2.Agent.Leave()
|
||||
|
||||
// Query again, should still contain expected output
|
||||
if code := cmd.Run([]string{"-address=" + url}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
if out := ui.OutputWriter.String(); !strings.Contains(out, name) {
|
||||
t.Fatalf("expected %q in output, got: %s", name, out)
|
||||
}
|
||||
code = cmd.Run([]string{"-address=" + url})
|
||||
must.Zero(t, code)
|
||||
|
||||
must.StrContains(t, ui.OutputWriter.String(), name)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,10 @@ capability.
|
|||
for each member. This mode reveals additional information not displayed in
|
||||
the standard output format.
|
||||
|
||||
- `-json`: Output the server memebers in its JSON format.
|
||||
|
||||
- `-t`: Format and display the server memebers using a Go template.
|
||||
|
||||
## Examples
|
||||
|
||||
Default view:
|
||||
|
@ -57,3 +61,44 @@ server-1.global 10.0.0.8 4648 alive true 2 3 1.3.0
|
|||
server-2.global 10.0.0.9 4648 alive false 2 3 1.3.0 dc1 global id=04594bee-fec9-4cec-f308-eebe82025ae7,dc=dc1,expect=3,rpc_addr=10.0.0.9,raft_vsn=3,port=4647,role=nomad,region=global,build=1.3.0
|
||||
server-3.global 10.0.0.10 4648 alive false 2 3 1.3.0 dc1 global region=global,dc=dc1,rpc_addr=10.0.0.10,raft_vsn=3,build=1.3.0,expect=3,id=59542f6c-fb0e-50f1-4c9f-98bb593e9fe8,role=nomad,port=4647
|
||||
```
|
||||
|
||||
The `-json` flag can be used to get the latest server members information in json format:
|
||||
|
||||
```shell-session
|
||||
$ nomad server members -json
|
||||
[
|
||||
{
|
||||
"Addr": "127.0.0.1",
|
||||
"DelegateCur": 4,
|
||||
"DelegateMax": 5,
|
||||
"DelegateMin": 2,
|
||||
"Name": "bacon-mac.global",
|
||||
"Port": 4648,
|
||||
"ProtocolCur": 2,
|
||||
"ProtocolMax": 5,
|
||||
"ProtocolMin": 1,
|
||||
"Status": "alive",
|
||||
"Tags": {
|
||||
"build": "1.5.0",
|
||||
"region": "global",
|
||||
"rpc_addr": "127.0.0.1",
|
||||
"role": "nomad",
|
||||
"raft_vsn": "3",
|
||||
"bootstrap": "1",
|
||||
"expect": "1",
|
||||
"dc": "dc1",
|
||||
"port": "4647",
|
||||
"revision": "fc40c491cacec3d8ec3f2f98cd82b9068a50797c",
|
||||
"vsn": "1",
|
||||
"id": "354fd9a8-4228-ca8e-8b93-3cf002514910"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Or use the `-t` flag to format and display the server members information using a Go template:
|
||||
|
||||
```shell-session
|
||||
$ nomad server members -t '{{range .}}{{printf "%s: %s" .Name .Status }}{{end}}'
|
||||
bacon-mac.global: alive
|
||||
```
|
Loading…
Reference in New Issue