commands: move catalog list nodes command to separate pkg

This commit is contained in:
Frank Schroeder 2017-10-11 16:45:29 +02:00 committed by Frank Schröder
parent bd73c4cecf
commit 117305eb4f
3 changed files with 95 additions and 76 deletions

View File

@ -1,7 +1,9 @@
package command
package catlistnodes
import (
"flag"
"fmt"
"sort"
"strings"
"github.com/hashicorp/consul/api"
@ -10,12 +12,16 @@ import (
"github.com/ryanuber/columnize"
)
var _ cli.Command = (*CatalogListNodesCommand)(nil)
func New(ui cli.Ui) *cmd {
c := &cmd{UI: ui}
c.initFlags()
return c
}
// CatalogListNodesCommand is a Command implementation that is used to fetch all the
// nodes in the catalog.
type CatalogListNodesCommand struct {
BaseCommand
type cmd struct {
UI cli.Ui
flags *flag.FlagSet
http *flags.HTTPFlags
// flags
detailed bool
@ -24,67 +30,37 @@ type CatalogListNodesCommand struct {
service string
}
func (c *CatalogListNodesCommand) initFlags() {
c.InitFlagSet()
c.FlagSet.BoolVar(&c.detailed, "detailed", false, "Output detailed information about "+
func (c *cmd) initFlags() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.BoolVar(&c.detailed, "detailed", false, "Output detailed information about "+
"the nodes including their addresses and metadata.")
c.FlagSet.StringVar(&c.near, "near", "", "Node name to sort the node list in ascending "+
c.flags.StringVar(&c.near, "near", "", "Node name to sort the node list in ascending "+
"order based on estimated round-trip time from that node. "+
"Passing \"_agent\" will use this agent's node for sorting.")
c.FlagSet.Var((*flags.FlagMapValue)(&c.nodeMeta), "node-meta", "Metadata to "+
c.flags.Var((*flags.FlagMapValue)(&c.nodeMeta), "node-meta", "Metadata to "+
"filter nodes with the given `key=value` pairs. This flag may be "+
"specified multiple times to filter on multiple sources of metadata.")
c.FlagSet.StringVar(&c.service, "service", "", "Service `id or name` to filter nodes. "+
c.flags.StringVar(&c.service, "service", "", "Service `id or name` to filter nodes. "+
"Only nodes which are providing the given service will be returned.")
c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags())
flags.Merge(c.flags, c.http.ServerFlags())
}
func (c *CatalogListNodesCommand) Help() string {
func (c *cmd) Run(args []string) int {
c.initFlags()
return c.HelpCommand(`
Usage: consul catalog nodes [options]
Retrieves the list nodes registered in a given datacenter. By default, the
datacenter of the local agent is queried.
To retrieve the list of nodes:
$ consul catalog nodes
To print detailed information including full node IDs, tagged addresses, and
metadata information:
$ consul catalog nodes -detailed
To list nodes which are running a particular service:
$ consul catalog nodes -service=web
To filter by node metadata:
$ consul catalog nodes -node-meta="foo=bar"
To sort nodes by estimated round-trip time from node-web:
$ consul catalog nodes -near=node-web
For a full list of options and examples, please see the Consul documentation.
`)
}
func (c *CatalogListNodesCommand) Run(args []string) int {
c.initFlags()
if err := c.FlagSet.Parse(args); err != nil {
if err := c.flags.Parse(args); err != nil {
return 1
}
if l := len(c.FlagSet.Args()); l > 0 {
if l := len(c.flags.Args()); l > 0 {
c.UI.Error(fmt.Sprintf("Too many arguments (expected 0, got %d)", l))
return 1
}
// Create and test the HTTP client
client, err := c.HTTPClient()
client, err := c.http.APIClient()
if err != nil {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
@ -142,10 +118,42 @@ func (c *CatalogListNodesCommand) Run(args []string) int {
return 0
}
func (c *CatalogListNodesCommand) Synopsis() string {
func (c *cmd) Synopsis() string {
return "Lists all nodes in the given datacenter"
}
func (c *cmd) Help() string {
s := `
Usage: consul catalog nodes [options]
Retrieves the list nodes registered in a given datacenter. By default, the
datacenter of the local agent is queried.
To retrieve the list of nodes:
$ consul catalog nodes
To print detailed information including full node IDs, tagged addresses, and
metadata information:
$ consul catalog nodes -detailed
To list nodes which are running a particular service:
$ consul catalog nodes -service=web
To filter by node metadata:
$ consul catalog nodes -node-meta="foo=bar"
To sort nodes by estimated round-trip time from node-web:
$ consul catalog nodes -near=node-web
For a full list of options and examples, please see the Consul documentation.`
return flags.Usage(s, c.flags, c.http.ClientFlags(), c.http.ServerFlags())
}
// printNodes accepts a list of nodes and prints information in a tabular
// format about the nodes.
func printNodes(nodes []*api.Node, detailed bool) (string, error) {
@ -191,3 +199,19 @@ func simpleNodes(nodes []*api.Node) []string {
return result
}
// mapToKV converts a map[string]string into a human-friendly key=value list,
// sorted by name.
func mapToKV(m map[string]string, joiner string) string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
r := make([]string, len(keys))
for i, k := range keys {
r[i] = fmt.Sprintf("%s=%s", k, m[k])
}
return strings.Join(r, joiner)
}

View File

@ -1,4 +1,4 @@
package command
package catlistnodes
import (
"strings"
@ -8,24 +8,17 @@ import (
"github.com/mitchellh/cli"
)
func testCatalogListNodesCommand(t *testing.T) (*cli.MockUi, *CatalogListNodesCommand) {
ui := cli.NewMockUi()
return ui, &CatalogListNodesCommand{
BaseCommand: BaseCommand{
Flags: FlagSetHTTP,
UI: ui,
},
}
}
func TestCatalogListNodesCommand_noTabs(t *testing.T) {
t.Parallel()
assertNoTabs(t, new(CatalogListNodesCommand))
if strings.ContainsRune(New(nil).Help(), '\t') {
t.Fatal("usage has tabs")
}
}
func TestCatalogListNodesCommand_Validation(t *testing.T) {
t.Parallel()
ui, c := testCatalogListNodesCommand(t)
ui := cli.NewMockUi()
c := New(ui)
cases := map[string]struct {
args []string
@ -64,7 +57,8 @@ func TestCatalogListNodesCommand_Run(t *testing.T) {
defer a.Shutdown()
t.Run("simple", func(t *testing.T) {
ui, c := testCatalogListNodesCommand(t)
ui := cli.NewMockUi()
c := New(ui)
args := []string{
"-http-addr=" + a.HTTPAddr(),
}
@ -87,7 +81,8 @@ func TestCatalogListNodesCommand_Run(t *testing.T) {
})
t.Run("detailed", func(t *testing.T) {
ui, c := testCatalogListNodesCommand(t)
ui := cli.NewMockUi()
c := New(ui)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-detailed",
@ -106,7 +101,8 @@ func TestCatalogListNodesCommand_Run(t *testing.T) {
})
t.Run("node-meta", func(t *testing.T) {
ui, c := testCatalogListNodesCommand(t)
ui := cli.NewMockUi()
c := New(ui)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-node-meta", "foo=bar",
@ -123,7 +119,8 @@ func TestCatalogListNodesCommand_Run(t *testing.T) {
})
t.Run("near", func(t *testing.T) {
ui, c := testCatalogListNodesCommand(t)
ui := cli.NewMockUi()
c := New(ui)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-near", "_agent",
@ -140,7 +137,8 @@ func TestCatalogListNodesCommand_Run(t *testing.T) {
})
t.Run("service_present", func(t *testing.T) {
ui, c := testCatalogListNodesCommand(t)
ui := cli.NewMockUi()
c := New(ui)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-service", "consul",
@ -157,7 +155,8 @@ func TestCatalogListNodesCommand_Run(t *testing.T) {
})
t.Run("service_missing", func(t *testing.T) {
ui, c := testCatalogListNodesCommand(t)
ui := cli.NewMockUi()
c := New(ui)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-service", "this-service-will-literally-never-exist",

View File

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/consul/command/cat"
"github.com/hashicorp/consul/command/catlistdc"
"github.com/hashicorp/consul/command/catlistnodes"
"github.com/hashicorp/consul/command/event"
execmd "github.com/hashicorp/consul/command/exec"
"github.com/hashicorp/consul/command/forceleave"
@ -56,12 +57,7 @@ func init() {
},
"catalog nodes": func() (cli.Command, error) {
return &CatalogListNodesCommand{
BaseCommand: BaseCommand{
Flags: FlagSetHTTP,
UI: ui,
},
}, nil
return catlistnodes.New(ui), nil
},
"catalog services": func() (cli.Command, error) {