Merge pull request #2726 from hashicorp/f-cli-rework-3

Centralize command-line parsing part 3
This commit is contained in:
Kyle Havlovitz 2017-02-10 13:42:01 -05:00 committed by GitHub
commit 5be21e3e24
38 changed files with 448 additions and 409 deletions

View File

@ -58,6 +58,14 @@ func (c *Command) HTTPClient() (*api.Client, error) {
return api.NewClient(config)
}
func (c *Command) HTTPAddr() string {
return c.httpAddr.String()
}
func (c *Command) HTTPToken() string {
return c.token.String()
}
func (c *Command) HTTPDatacenter() string {
return c.datacenter.String()
}

View File

@ -2,10 +2,11 @@ package command
import (
"fmt"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
func testJoinCommand(t *testing.T) (*cli.MockUi, *JoinCommand) {

View File

@ -1,53 +1,47 @@
package command
import (
"flag"
"fmt"
"github.com/mitchellh/cli"
"github.com/hashicorp/consul/command/base"
"strings"
)
// LeaveCommand is a Command implementation that instructs
// the Consul agent to gracefully leave the cluster
type LeaveCommand struct {
Ui cli.Ui
base.Command
}
func (c *LeaveCommand) Help() string {
helpText := `
Usage: consul leave
Usage: consul leave [options]
Causes the agent to gracefully leave the Consul cluster and shutdown.
Options:
` + c.Command.Help()
-rpc-addr=127.0.0.1:8400 RPC address of the Consul agent.
`
return strings.TrimSpace(helpText)
}
func (c *LeaveCommand) Run(args []string) int {
cmdFlags := flag.NewFlagSet("leave", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
rpcAddr := RPCAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
f := c.Command.NewFlagSet(c)
if err := c.Command.Parse(args); err != nil {
return 1
}
nonFlagArgs := cmdFlags.Args()
nonFlagArgs := f.Args()
if len(nonFlagArgs) > 0 {
c.Ui.Error(fmt.Sprintf("Error found unexpected args: %v", nonFlagArgs))
c.Ui.Output(c.Help())
return 1
}
client, err := RPCClient(*rpcAddr)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}
defer client.Close()
if err := client.Leave(); err != nil {
if err := client.Agent().Leave(); err != nil {
c.Ui.Error(fmt.Sprintf("Error leaving: %s", err))
return 1
}

View File

@ -1,11 +1,22 @@
package command
import (
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
"strings"
"testing"
)
func testLeaveCommand(t *testing.T) (*cli.MockUi, *LeaveCommand) {
ui := new(cli.MockUi)
return ui, &LeaveCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetClientHTTP,
},
}
}
func TestLeaveCommand_implements(t *testing.T) {
var _ cli.Command = &LeaveCommand{}
}
@ -14,9 +25,8 @@ func TestLeaveCommandRun(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &LeaveCommand{Ui: ui}
args := []string{"-rpc-addr=" + a1.addr}
ui, c := testLeaveCommand(t)
args := []string{"-http-addr=" + a1.httpAddr}
code := c.Run(args)
if code != 0 {
@ -32,9 +42,8 @@ func TestLeaveCommandFailOnNonFlagArgs(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &LeaveCommand{Ui: ui}
args := []string{"-rpc-addr=" + a1.addr, "appserver1"}
_, c := testLeaveCommand(t)
args := []string{"-http-addr=" + a1.httpAddr, "appserver1"}
code := c.Run(args)
if code == 0 {

View File

@ -1,18 +1,16 @@
package command
import (
"flag"
"fmt"
"strings"
"github.com/hashicorp/consul/api"
"github.com/mitchellh/cli"
"github.com/hashicorp/consul/command/base"
)
// MaintCommand is a Command implementation that enables or disables
// node or service maintenance mode.
type MaintCommand struct {
Ui cli.Ui
base.Command
}
func (c *MaintCommand) Help() string {
@ -40,15 +38,8 @@ Usage: consul maint [options]
If no arguments are given, the agent's maintenance status will be shown.
This will return blank if nothing is currently under maintenance.
Options:
` + c.Command.Help()
-enable Enable maintenance mode.
-disable Disable maintenance mode.
-reason=<string> Text string describing the maintenance reason
-service=<serviceID> Control maintenance mode for a specific service ID
-token="" ACL token to use. Defaults to that of agent.
-http-addr=127.0.0.1:8500 HTTP address of the Consul agent.
`
return strings.TrimSpace(helpText)
}
@ -57,19 +48,15 @@ func (c *MaintCommand) Run(args []string) int {
var disable bool
var reason string
var serviceID string
var token string
cmdFlags := flag.NewFlagSet("maint", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
f := c.Command.NewFlagSet(c)
cmdFlags.BoolVar(&enable, "enable", false, "enable maintenance mode")
cmdFlags.BoolVar(&disable, "disable", false, "disable maintenance mode")
cmdFlags.StringVar(&reason, "reason", "", "maintenance reason")
cmdFlags.StringVar(&serviceID, "service", "", "service maintenance")
cmdFlags.StringVar(&token, "token", "", "")
httpAddr := HTTPAddrFlag(cmdFlags)
f.BoolVar(&enable, "enable", false, "Enable maintenance mode.")
f.BoolVar(&disable, "disable", false, "Disable maintenance mode.")
f.StringVar(&reason, "reason", "", "Text describing the maintenance reason.")
f.StringVar(&serviceID, "service", "", "Control maintenance mode for a specific service ID.")
if err := cmdFlags.Parse(args); err != nil {
if err := c.Command.Parse(args); err != nil {
return 1
}
@ -88,10 +75,7 @@ func (c *MaintCommand) Run(args []string) int {
}
// Create and test the HTTP client
conf := api.DefaultConfig()
conf.Address = *httpAddr
conf.Token = token
client, err := api.NewClient(conf)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1

View File

@ -4,17 +4,27 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/consul/structs"
"github.com/mitchellh/cli"
)
func testMaintCommand(t *testing.T) (*cli.MockUi, *MaintCommand) {
ui := new(cli.MockUi)
return ui, &MaintCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetClientHTTP,
},
}
}
func TestMaintCommand_implements(t *testing.T) {
var _ cli.Command = &MaintCommand{}
}
func TestMaintCommandRun_ConflictingArgs(t *testing.T) {
ui := new(cli.MockUi)
c := &MaintCommand{Ui: ui}
_, c := testMaintCommand(t)
if code := c.Run([]string{"-enable", "-disable"}); code != 1 {
t.Fatalf("expected return code 1, got %d", code)
@ -53,8 +63,7 @@ func TestMaintCommandRun_NoArgs(t *testing.T) {
a1.agent.EnableNodeMaintenance("broken 2", "")
// Run consul maint with no args (list mode)
ui := new(cli.MockUi)
c := &MaintCommand{Ui: ui}
ui, c := testMaintCommand(t)
args := []string{"-http-addr=" + a1.httpAddr}
code := c.Run(args)
@ -84,8 +93,7 @@ func TestMaintCommandRun_EnableNodeMaintenance(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &MaintCommand{Ui: ui}
ui, c := testMaintCommand(t)
args := []string{
"-http-addr=" + a1.httpAddr,
@ -106,8 +114,7 @@ func TestMaintCommandRun_DisableNodeMaintenance(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &MaintCommand{Ui: ui}
ui, c := testMaintCommand(t)
args := []string{
"-http-addr=" + a1.httpAddr,
@ -136,8 +143,7 @@ func TestMaintCommandRun_EnableServiceMaintenance(t *testing.T) {
t.Fatalf("err: %v", err)
}
ui := new(cli.MockUi)
c := &MaintCommand{Ui: ui}
ui, c := testMaintCommand(t)
args := []string{
"-http-addr=" + a1.httpAddr,
@ -168,8 +174,7 @@ func TestMaintCommandRun_DisableServiceMaintenance(t *testing.T) {
t.Fatalf("err: %v", err)
}
ui := new(cli.MockUi)
c := &MaintCommand{Ui: ui}
ui, c := testMaintCommand(t)
args := []string{
"-http-addr=" + a1.httpAddr,
@ -190,8 +195,7 @@ func TestMaintCommandRun_ServiceMaintenance_NoService(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &MaintCommand{Ui: ui}
ui, c := testMaintCommand(t)
args := []string{
"-http-addr=" + a1.httpAddr,

View File

@ -1,21 +1,22 @@
package command
import (
"flag"
"fmt"
"github.com/hashicorp/consul/command/agent"
"github.com/mitchellh/cli"
"github.com/ryanuber/columnize"
"net"
"regexp"
"sort"
"strings"
consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/serf/serf"
"github.com/ryanuber/columnize"
)
// MembersCommand is a Command implementation that queries a running
// Consul agent what members are part of the cluster currently.
type MembersCommand struct {
Ui cli.Ui
base.Command
}
func (c *MembersCommand) Help() string {
@ -24,18 +25,8 @@ Usage: consul members [options]
Outputs the members of a running Consul agent.
Options:
` + c.Command.Help()
-detailed Provides detailed information about nodes
-rpc-addr=127.0.0.1:8400 RPC address of the Consul agent.
-status=<regexp> If provided, output is filtered to only nodes matching
the regular expression for status
-wan If the agent is in server mode, this can be used to return
the other peers in the WAN pool
`
return strings.TrimSpace(helpText)
}
@ -43,13 +34,18 @@ func (c *MembersCommand) Run(args []string) int {
var detailed bool
var wan bool
var statusFilter string
cmdFlags := flag.NewFlagSet("members", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
cmdFlags.BoolVar(&detailed, "detailed", false, "detailed output")
cmdFlags.BoolVar(&wan, "wan", false, "wan members")
cmdFlags.StringVar(&statusFilter, "status", ".*", "status filter")
rpcAddr := RPCAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
f := c.Command.NewFlagSet(c)
f.BoolVar(&detailed, "detailed", false,
"Provides detailed information about nodes.")
f.BoolVar(&wan, "wan", false,
"If the agent is in server mode, this can be used to return the other "+
"peers in the WAN pool.")
f.StringVar(&statusFilter, "status", ".*",
"If provided, output is filtered to only nodes matching the regular "+
"expression for status.")
if err := c.Command.Parse(args); err != nil {
return 1
}
@ -60,19 +56,13 @@ func (c *MembersCommand) Run(args []string) int {
return 1
}
client, err := RPCClient(*rpcAddr)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}
defer client.Close()
var members []agent.Member
if wan {
members, err = client.WANMembers()
} else {
members, err = client.LANMembers()
}
members, err := client.Agent().Members(wan)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error retrieving members: %s", err))
return 1
@ -82,7 +72,8 @@ func (c *MembersCommand) Run(args []string) int {
n := len(members)
for i := 0; i < n; i++ {
member := members[i]
if !statusRe.MatchString(member.Status) {
statusString := serf.MemberStatus(member.Status).String()
if !statusRe.MatchString(statusString) {
members[i], members[n-1] = members[n-1], members[i]
i--
n--
@ -114,7 +105,7 @@ func (c *MembersCommand) Run(args []string) int {
}
// so we can sort members by name
type ByMemberName []agent.Member
type ByMemberName []*consulapi.AgentMember
func (m ByMemberName) Len() int { return len(m) }
func (m ByMemberName) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
@ -122,12 +113,12 @@ func (m ByMemberName) Less(i, j int) bool { return m[i].Name < m[j].Name }
// standardOutput is used to dump the most useful information about nodes
// in a more human-friendly format
func (c *MembersCommand) standardOutput(members []agent.Member) []string {
func (c *MembersCommand) standardOutput(members []*consulapi.AgentMember) []string {
result := make([]string, 0, len(members))
header := "Node|Address|Status|Type|Build|Protocol|DC"
result = append(result, header)
for _, member := range members {
addr := net.TCPAddr{IP: member.Addr, Port: int(member.Port)}
addr := net.TCPAddr{IP: net.ParseIP(member.Addr), Port: int(member.Port)}
protocol := member.Tags["vsn"]
build := member.Tags["build"]
if build == "" {
@ -137,18 +128,19 @@ func (c *MembersCommand) standardOutput(members []agent.Member) []string {
}
dc := member.Tags["dc"]
statusString := serf.MemberStatus(member.Status).String()
switch member.Tags["role"] {
case "node":
line := fmt.Sprintf("%s|%s|%s|client|%s|%s|%s",
member.Name, addr.String(), member.Status, build, protocol, dc)
member.Name, addr.String(), statusString, build, protocol, dc)
result = append(result, line)
case "consul":
line := fmt.Sprintf("%s|%s|%s|server|%s|%s|%s",
member.Name, addr.String(), member.Status, build, protocol, dc)
member.Name, addr.String(), statusString, build, protocol, dc)
result = append(result, line)
default:
line := fmt.Sprintf("%s|%s|%s|unknown|||",
member.Name, addr.String(), member.Status)
member.Name, addr.String(), statusString)
result = append(result, line)
}
}
@ -157,7 +149,7 @@ func (c *MembersCommand) standardOutput(members []agent.Member) []string {
// detailedOutput is used to dump all known information about nodes in
// their raw format
func (c *MembersCommand) detailedOutput(members []agent.Member) []string {
func (c *MembersCommand) detailedOutput(members []*consulapi.AgentMember) []string {
result := make([]string, 0, len(members))
header := "Node|Address|Status|Tags"
result = append(result, header)
@ -177,9 +169,9 @@ func (c *MembersCommand) detailedOutput(members []agent.Member) []string {
tags := strings.Join(tagPairs, ",")
addr := net.TCPAddr{IP: member.Addr, Port: int(member.Port)}
addr := net.TCPAddr{IP: net.ParseIP(member.Addr), Port: int(member.Port)}
line := fmt.Sprintf("%s|%s|%s|%s",
member.Name, addr.String(), member.Status, tags)
member.Name, addr.String(), serf.MemberStatus(member.Status).String(), tags)
result = append(result, line)
}
return result

View File

@ -2,11 +2,22 @@ package command
import (
"fmt"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
"strings"
"testing"
)
func testMembersCommand(t *testing.T) (*cli.MockUi, *MembersCommand) {
ui := new(cli.MockUi)
return ui, &MembersCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetClientHTTP,
},
}
}
func TestMembersCommand_implements(t *testing.T) {
var _ cli.Command = &MembersCommand{}
}
@ -15,9 +26,8 @@ func TestMembersCommandRun(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &MembersCommand{Ui: ui}
args := []string{"-rpc-addr=" + a1.addr}
ui, c := testMembersCommand(t)
args := []string{"-http-addr=" + a1.httpAddr}
code := c.Run(args)
if code != 0 {
@ -44,9 +54,8 @@ func TestMembersCommandRun_WAN(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &MembersCommand{Ui: ui}
args := []string{"-rpc-addr=" + a1.addr, "-wan"}
ui, c := testMembersCommand(t)
args := []string{"-http-addr=" + a1.httpAddr, "-wan"}
code := c.Run(args)
if code != 0 {
@ -62,10 +71,9 @@ func TestMembersCommandRun_statusFilter(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &MembersCommand{Ui: ui}
ui, c := testMembersCommand(t)
args := []string{
"-rpc-addr=" + a1.addr,
"-http-addr=" + a1.httpAddr,
"-status=a.*e",
}
@ -83,10 +91,9 @@ func TestMembersCommandRun_statusFilter_failed(t *testing.T) {
a1 := testAgent(t)
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &MembersCommand{Ui: ui}
ui, c := testMembersCommand(t)
args := []string{
"-rpc-addr=" + a1.addr,
"-http-addr=" + a1.httpAddr,
"-status=(fail|left)",
}

View File

@ -1,19 +1,19 @@
package command
import (
"flag"
"fmt"
"github.com/hashicorp/logutils"
"github.com/mitchellh/cli"
"strings"
"sync"
"github.com/hashicorp/consul/command/base"
)
// MonitorCommand is a Command implementation that queries a running
// Consul agent what members are part of the cluster currently.
type MonitorCommand struct {
base.Command
ShutdownCh <-chan struct{}
Ui cli.Ui
lock sync.Mutex
quitting bool
@ -29,40 +29,34 @@ Usage: consul monitor [options]
example your agent may only be logging at INFO level, but with the monitor
you can see the DEBUG level logs.
Options:
` + c.Command.Help()
-log-level=info Log level of the agent.
-rpc-addr=127.0.0.1:8400 RPC address of the Consul agent.
`
return strings.TrimSpace(helpText)
}
func (c *MonitorCommand) Run(args []string) int {
var logLevel string
cmdFlags := flag.NewFlagSet("monitor", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
cmdFlags.StringVar(&logLevel, "log-level", "INFO", "log level")
rpcAddr := RPCAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
f := c.Command.NewFlagSet(c)
f.StringVar(&logLevel, "log-level", "INFO", "Log level of the agent.")
if err := c.Command.Parse(args); err != nil {
return 1
}
client, err := RPCClient(*rpcAddr)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}
defer client.Close()
logCh := make(chan string, 1024)
monHandle, err := client.Monitor(logutils.LogLevel(logLevel), logCh)
eventDoneCh := make(chan struct{})
logCh, err := client.Agent().Monitor(logLevel, eventDoneCh, nil)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error starting monitor: %s", err))
return 1
}
defer client.Stop(monHandle)
eventDoneCh := make(chan struct{})
go func() {
defer close(eventDoneCh)
OUTER:

View File

@ -1,24 +1,24 @@
package command
import (
"flag"
"fmt"
"strings"
"flag"
"github.com/hashicorp/consul/api"
"github.com/mitchellh/cli"
"github.com/hashicorp/consul/command/base"
"github.com/ryanuber/columnize"
)
// OperatorCommand is used to provide various low-level tools for Consul
// operators.
type OperatorCommand struct {
Ui cli.Ui
base.Command
}
func (c *OperatorCommand) Help() string {
helpText := `
Usage: consul operator <subcommand> [common options] [action] [options]
Usage: consul operator <subcommand> [action] [options]
Provides cluster-level tools for Consul operators, such as interacting with
the Raft subsystem. NOTE: Use this command with extreme caution, as improper
@ -31,11 +31,6 @@ Usage: consul operator <subcommand> [common options] [action] [options]
Run consul operator <subcommand> with no arguments for help on that
subcommand.
Common Options:
-http-addr=127.0.0.1:8500 HTTP address of the Consul agent.
-token="" ACL token to use. Defaults to that of agent.
Subcommands:
raft View and modify Consul's Raft configuration.
@ -73,17 +68,15 @@ func (c *OperatorCommand) Synopsis() string {
}
const raftHelp = `
Raft Subcommand Actions:
Operator Raft Subcommand:
raft -list-peers -stale=[true|false]
The raft subcommand can be used in two modes:
consul operator raft -list-peers
Displays the current Raft peer configuration.
The -stale argument defaults to "false" which means the leader provides the
result. If the cluster is in an outage state without a leader, you may need
to set -stale to "true" to get the configuration from a non-leader server.
raft -remove-peer -address="IP:port"
consul operator raft -remove-peer -address="IP:port"
Removes Consul server with given -address from the Raft configuration.
@ -93,33 +86,39 @@ Raft Subcommand Actions:
affects the Raft quorum. If the server still shows in the output of the
"consul members" command, it is preferable to clean up by simply running
"consul force-leave" instead of this command.
`
// raft handles the raft subcommands.
func (c *OperatorCommand) raft(args []string) error {
cmdFlags := flag.NewFlagSet("raft", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
f := c.Command.NewFlagSet(c)
// Parse verb arguments.
var listPeers, removePeer bool
cmdFlags.BoolVar(&listPeers, "list-peers", false, "")
cmdFlags.BoolVar(&removePeer, "remove-peer", false, "")
f.BoolVar(&listPeers, "list-peers", false,
"If this flag is provided, the current Raft peer configuration will be "+
"displayed. If the cluster is in an outage state without a leader, you may need "+
"to set -stale to 'true' to get the configuration from a non-leader server.")
f.BoolVar(&removePeer, "remove-peer", false,
"If this flag is provided, the Consul server with the given -address will be "+
"removed from the Raft configuration.")
// Parse other arguments.
var stale bool
var address, token string
cmdFlags.StringVar(&address, "address", "", "")
cmdFlags.BoolVar(&stale, "stale", false, "")
cmdFlags.StringVar(&token, "token", "", "")
httpAddr := HTTPAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
var address string
f.StringVar(&address, "address", "",
"The address to remove from the Raft configuration.")
if err := c.Command.Parse(args); err != nil {
if err == flag.ErrHelp {
c.Ui.Output("")
c.Ui.Output(strings.TrimSpace(raftHelp + c.Command.Help()))
return nil
}
return err
}
// Set up a client.
conf := api.DefaultConfig()
conf.Address = *httpAddr
client, err := api.NewClient(conf)
client, err := c.Command.HTTPClient()
if err != nil {
return fmt.Errorf("error connecting to Consul agent: %s", err)
}
@ -129,8 +128,7 @@ func (c *OperatorCommand) raft(args []string) error {
if listPeers {
// Fetch the current configuration.
q := &api.QueryOptions{
AllowStale: stale,
Token: token,
AllowStale: c.Command.HTTPStale(),
}
reply, err := operator.RaftGetConfiguration(q)
if err != nil {
@ -156,17 +154,14 @@ func (c *OperatorCommand) raft(args []string) error {
}
// Try to kick the peer.
w := &api.WriteOptions{
Token: token,
}
if err := operator.RaftRemovePeerByAddress(address, w); err != nil {
if err := operator.RaftRemovePeerByAddress(address, nil); err != nil {
return err
}
c.Ui.Output(fmt.Sprintf("Removed peer with address %q", address))
} else {
c.Ui.Output(c.Help())
c.Ui.Output("")
c.Ui.Output(strings.TrimSpace(raftHelp))
c.Ui.Output(strings.TrimSpace(raftHelp + c.Command.Help()))
}
return nil

View File

@ -4,9 +4,20 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
func testOperatorCommand(t *testing.T) (*cli.MockUi, *OperatorCommand) {
ui := new(cli.MockUi)
return ui, &OperatorCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetHTTP,
},
}
}
func TestOperator_Implements(t *testing.T) {
var _ cli.Command = &OperatorCommand{}
}
@ -16,8 +27,7 @@ func TestOperator_Raft_ListPeers(t *testing.T) {
defer a1.Shutdown()
waitForLeader(t, a1.httpAddr)
ui := new(cli.MockUi)
c := &OperatorCommand{Ui: ui}
ui, c := testOperatorCommand(t)
args := []string{"raft", "-http-addr=" + a1.httpAddr, "-list-peers"}
code := c.Run(args)
@ -35,8 +45,7 @@ func TestOperator_Raft_RemovePeer(t *testing.T) {
defer a1.Shutdown()
waitForLeader(t, a1.httpAddr)
ui := new(cli.MockUi)
c := &OperatorCommand{Ui: ui}
ui, c := testOperatorCommand(t)
args := []string{"raft", "-http-addr=" + a1.httpAddr, "-remove-peer", "-address=nope"}
code := c.Run(args)

View File

@ -1,16 +1,15 @@
package command
import (
"flag"
"fmt"
"github.com/mitchellh/cli"
"github.com/hashicorp/consul/command/base"
"strings"
)
// ReloadCommand is a Command implementation that instructs
// the Consul agent to reload configurations
type ReloadCommand struct {
Ui cli.Ui
base.Command
}
func (c *ReloadCommand) Help() string {
@ -20,29 +19,25 @@ Usage: consul reload
Causes the agent to reload configurations. This can be used instead
of sending the SIGHUP signal to the agent.
Options:
` + c.Command.Help()
-rpc-addr=127.0.0.1:8400 RPC address of the Consul agent.
`
return strings.TrimSpace(helpText)
}
func (c *ReloadCommand) Run(args []string) int {
cmdFlags := flag.NewFlagSet("reload", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
rpcAddr := RPCAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
c.Command.NewFlagSet(c)
if err := c.Command.Parse(args); err != nil {
return 1
}
client, err := RPCClient(*rpcAddr)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}
defer client.Close()
if err := client.Reload(); err != nil {
if err := client.Agent().Reload(); err != nil {
c.Ui.Error(fmt.Sprintf("Error reloading: %s", err))
return 1
}

View File

@ -1,9 +1,11 @@
package command
import (
"github.com/mitchellh/cli"
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
func TestReloadCommand_implements(t *testing.T) {
@ -11,12 +13,24 @@ func TestReloadCommand_implements(t *testing.T) {
}
func TestReloadCommandRun(t *testing.T) {
a1 := testAgent(t)
reloadCh := make(chan chan error)
a1 := testAgentWithConfigReload(t, nil, reloadCh)
defer a1.Shutdown()
// Setup a dummy response to errCh to simulate a successful reload
go func() {
errCh := <-reloadCh
errCh <- nil
}()
ui := new(cli.MockUi)
c := &ReloadCommand{Ui: ui}
args := []string{"-rpc-addr=" + a1.addr}
c := &ReloadCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetClientHTTP,
},
}
args := []string{"-http-addr=" + a1.httpAddr}
code := c.Run(args)
if code != 0 {

View File

@ -1,19 +1,17 @@
package command
import (
"flag"
"fmt"
"strings"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/serf/coordinate"
"github.com/mitchellh/cli"
)
// RTTCommand is a Command implementation that allows users to query the
// estimated round trip time between nodes using network coordinates.
type RTTCommand struct {
Ui cli.Ui
base.Command
}
func (c *RTTCommand) Help() string {
@ -36,28 +34,24 @@ Usage: consul rtt [options] node1 [node2]
because they are maintained by independent Serf gossip pools, so they are
not compatible.
Options:
` + c.Command.Help()
-wan Use WAN coordinates instead of LAN coordinates.
-http-addr=127.0.0.1:8500 HTTP address of the Consul agent.
`
return strings.TrimSpace(helpText)
}
func (c *RTTCommand) Run(args []string) int {
var wan bool
cmdFlags := flag.NewFlagSet("rtt", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
f := c.Command.NewFlagSet(c)
cmdFlags.BoolVar(&wan, "wan", false, "wan")
httpAddr := HTTPAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
f.BoolVar(&wan, "wan", false, "Use WAN coordinates instead of LAN coordinates.")
if err := c.Command.Parse(args); err != nil {
return 1
}
// They must provide at least one node.
nodes := cmdFlags.Args()
nodes := f.Args()
if len(nodes) < 1 || len(nodes) > 2 {
c.Ui.Error("One or two node names must be specified")
c.Ui.Error("")
@ -66,9 +60,7 @@ func (c *RTTCommand) Run(args []string) int {
}
// Create and test the HTTP client.
conf := api.DefaultConfig()
conf.Address = *httpAddr
client, err := api.NewClient(conf)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1

View File

@ -7,19 +7,29 @@ import (
"time"
"github.com/hashicorp/consul/command/agent"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/testutil"
"github.com/hashicorp/serf/coordinate"
"github.com/mitchellh/cli"
)
func testRTTCommand(t *testing.T) (*cli.MockUi, *RTTCommand) {
ui := new(cli.MockUi)
return ui, &RTTCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetClientHTTP,
},
}
}
func TestRTTCommand_Implements(t *testing.T) {
var _ cli.Command = &RTTCommand{}
}
func TestRTTCommand_Run_BadArgs(t *testing.T) {
ui := new(cli.MockUi)
c := &RTTCommand{Ui: ui}
_, c := testRTTCommand(t)
if code := c.Run([]string{}); code != 1 {
t.Fatalf("expected return code 1, got %d", code)
@ -90,8 +100,7 @@ func TestRTTCommand_Run_LAN(t *testing.T) {
}
// Ask for the RTT of two known nodes
ui := new(cli.MockUi)
c := &RTTCommand{Ui: ui}
ui, c := testRTTCommand(t)
args := []string{
"-http-addr=" + a.httpAddr,
a.config.NodeName,
@ -118,8 +127,7 @@ func TestRTTCommand_Run_LAN(t *testing.T) {
// Default to the agent's node.
{
ui := new(cli.MockUi)
c := &RTTCommand{Ui: ui}
ui, c := testRTTCommand(t)
args := []string{
"-http-addr=" + a.httpAddr,
"dogs",
@ -138,8 +146,7 @@ func TestRTTCommand_Run_LAN(t *testing.T) {
// Try an unknown node.
{
ui := new(cli.MockUi)
c := &RTTCommand{Ui: ui}
ui, c := testRTTCommand(t)
args := []string{
"-http-addr=" + a.httpAddr,
a.config.NodeName,
@ -162,8 +169,7 @@ func TestRTTCommand_Run_WAN(t *testing.T) {
// We can't easily inject WAN coordinates, so we will just query the
// node with itself.
{
ui := new(cli.MockUi)
c := &RTTCommand{Ui: ui}
ui, c := testRTTCommand(t)
args := []string{
"-wan",
"-http-addr=" + a.httpAddr,
@ -183,8 +189,7 @@ func TestRTTCommand_Run_WAN(t *testing.T) {
// Default to the agent's node.
{
ui := new(cli.MockUi)
c := &RTTCommand{Ui: ui}
ui, c := testRTTCommand(t)
args := []string{
"-wan",
"-http-addr=" + a.httpAddr,
@ -203,8 +208,7 @@ func TestRTTCommand_Run_WAN(t *testing.T) {
// Try an unknown node.
{
ui := new(cli.MockUi)
c := &RTTCommand{Ui: ui}
ui, c := testRTTCommand(t)
args := []string{
"-wan",
"-http-addr=" + a.httpAddr,

View File

@ -2,20 +2,19 @@ package command
import (
"bytes"
"flag"
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/snapshot"
"github.com/mitchellh/cli"
)
// SnapshotInspectCommand is a Command implementation that is used to display
// metadata about a snapshot file
type SnapshotInspectCommand struct {
Ui cli.Ui
base.Command
}
func (c *SnapshotInspectCommand) Help() string {
@ -35,15 +34,15 @@ Usage: consul snapshot inspect [options] FILE
}
func (c *SnapshotInspectCommand) Run(args []string) int {
cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
flagSet := c.Command.NewFlagSet(c)
if err := c.Command.Parse(args); err != nil {
return 1
}
var file string
args = cmdFlags.Args()
args = flagSet.Args()
switch len(args) {
case 0:
c.Ui.Error("Missing FILE argument")

View File

@ -8,9 +8,20 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
func testSnapshotInspectCommand(t *testing.T) (*cli.MockUi, *SnapshotInspectCommand) {
ui := new(cli.MockUi)
return ui, &SnapshotInspectCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetNone,
},
}
}
func TestSnapshotInspectCommand_implements(t *testing.T) {
var _ cli.Command = &SnapshotInspectCommand{}
}
@ -20,8 +31,7 @@ func TestSnapshotInspectCommand_noTabs(t *testing.T) {
}
func TestSnapshotInspectCommand_Validation(t *testing.T) {
ui := new(cli.MockUi)
c := &SnapshotInspectCommand{Ui: ui}
ui, c := testSnapshotInspectCommand(t)
cases := map[string]struct {
args []string
@ -63,8 +73,6 @@ func TestSnapshotInspectCommand_Run(t *testing.T) {
defer srv.Shutdown()
waitForLeader(t, srv.httpAddr)
ui := new(cli.MockUi)
dir, err := ioutil.TempDir("", "snapshot")
if err != nil {
t.Fatalf("err: %v", err)
@ -93,10 +101,10 @@ func TestSnapshotInspectCommand_Run(t *testing.T) {
}
// Inspect the snapshot
inspect := &SnapshotInspectCommand{Ui: ui}
ui, c := testSnapshotInspectCommand(t)
args := []string{file}
code := inspect.Run(args)
code := c.Run(args)
if code != 0 {
t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String())
}

View File

@ -1,19 +1,17 @@
package command
import (
"flag"
"fmt"
"os"
"strings"
"github.com/hashicorp/consul/api"
"github.com/mitchellh/cli"
"github.com/hashicorp/consul/command/base"
)
// SnapshotRestoreCommand is a Command implementation that is used to restore
// the state of the Consul servers for disaster recovery.
type SnapshotRestoreCommand struct {
Ui cli.Ui
base.Command
}
func (c *SnapshotRestoreCommand) Help() string {
@ -38,24 +36,21 @@ Usage: consul snapshot restore [options] FILE
For a full list of options and examples, please see the Consul documentation.
` + apiOptsText
` + c.Command.Help()
return strings.TrimSpace(helpText)
}
func (c *SnapshotRestoreCommand) Run(args []string) int {
cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
datacenter := cmdFlags.String("datacenter", "", "")
token := cmdFlags.String("token", "", "")
httpAddr := HTTPAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
flagSet := c.Command.NewFlagSet(c)
if err := c.Command.Parse(args); err != nil {
return 1
}
var file string
args = cmdFlags.Args()
args = flagSet.Args()
switch len(args) {
case 0:
c.Ui.Error("Missing FILE argument")
@ -68,11 +63,7 @@ func (c *SnapshotRestoreCommand) Run(args []string) int {
}
// Create and test the HTTP client
conf := api.DefaultConfig()
conf.Datacenter = *datacenter
conf.Address = *httpAddr
conf.Token = *token
client, err := api.NewClient(conf)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1

View File

@ -8,9 +8,20 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
func testSnapshotRestoreCommand(t *testing.T) (*cli.MockUi, *SnapshotRestoreCommand) {
ui := new(cli.MockUi)
return ui, &SnapshotRestoreCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetHTTP,
},
}
}
func TestSnapshotRestoreCommand_implements(t *testing.T) {
var _ cli.Command = &SnapshotRestoreCommand{}
}
@ -20,8 +31,7 @@ func TestSnapshotRestoreCommand_noTabs(t *testing.T) {
}
func TestSnapshotRestoreCommand_Validation(t *testing.T) {
ui := new(cli.MockUi)
c := &SnapshotRestoreCommand{Ui: ui}
ui, c := testSnapshotRestoreCommand(t)
cases := map[string]struct {
args []string
@ -63,8 +73,7 @@ func TestSnapshotRestoreCommand_Run(t *testing.T) {
defer srv.Shutdown()
waitForLeader(t, srv.httpAddr)
ui := new(cli.MockUi)
c := &SnapshotSaveCommand{Ui: ui}
ui, c := testSnapshotRestoreCommand(t)
dir, err := ioutil.TempDir("", "snapshot")
if err != nil {

View File

@ -1,21 +1,20 @@
package command
import (
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/snapshot"
"github.com/mitchellh/cli"
)
// SnapshotSaveCommand is a Command implementation that is used to save the
// state of the Consul servers for disaster recovery.
type SnapshotSaveCommand struct {
Ui cli.Ui
base.Command
}
func (c *SnapshotSaveCommand) Help() string {
@ -40,25 +39,21 @@ Usage: consul snapshot save [options] FILE
For a full list of options and examples, please see the Consul documentation.
` + apiOptsText
` + c.Command.Help()
return strings.TrimSpace(helpText)
}
func (c *SnapshotSaveCommand) Run(args []string) int {
cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
datacenter := cmdFlags.String("datacenter", "", "")
token := cmdFlags.String("token", "", "")
stale := cmdFlags.Bool("stale", false, "")
httpAddr := HTTPAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
flagSet := c.Command.NewFlagSet(c)
if err := c.Command.Parse(args); err != nil {
return 1
}
var file string
args = cmdFlags.Args()
args = flagSet.Args()
switch len(args) {
case 0:
c.Ui.Error("Missing FILE argument")
@ -71,11 +66,7 @@ func (c *SnapshotSaveCommand) Run(args []string) int {
}
// Create and test the HTTP client
conf := api.DefaultConfig()
conf.Datacenter = *datacenter
conf.Address = *httpAddr
conf.Token = *token
client, err := api.NewClient(conf)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
@ -83,7 +74,7 @@ func (c *SnapshotSaveCommand) Run(args []string) int {
// Take the snapshot.
snap, qm, err := client.Snapshot().Save(&api.QueryOptions{
AllowStale: *stale,
AllowStale: c.Command.HTTPStale(),
})
if err != nil {
c.Ui.Error(fmt.Sprintf("Error saving snapshot: %s", err))

View File

@ -7,9 +7,20 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
func testSnapshotSaveCommand(t *testing.T) (*cli.MockUi, *SnapshotSaveCommand) {
ui := new(cli.MockUi)
return ui, &SnapshotSaveCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetHTTP,
},
}
}
func TestSnapshotSaveCommand_implements(t *testing.T) {
var _ cli.Command = &SnapshotSaveCommand{}
}
@ -19,8 +30,7 @@ func TestSnapshotSaveCommand_noTabs(t *testing.T) {
}
func TestSnapshotSaveCommand_Validation(t *testing.T) {
ui := new(cli.MockUi)
c := &SnapshotSaveCommand{Ui: ui}
ui, c := testSnapshotSaveCommand(t)
cases := map[string]struct {
args []string
@ -62,8 +72,7 @@ func TestSnapshotSaveCommand_Run(t *testing.T) {
defer srv.Shutdown()
waitForLeader(t, srv.httpAddr)
ui := new(cli.MockUi)
c := &SnapshotSaveCommand{Ui: ui}
ui, c := testSnapshotSaveCommand(t)
dir, err := ioutil.TempDir("", "snapshot")
if err != nil {

View File

@ -44,7 +44,7 @@ func (a *agentWrapper) Shutdown() {
}
func testAgent(t *testing.T) *agentWrapper {
return testAgentWithConfig(t, func(c *agent.Config) {})
return testAgentWithConfig(t, nil)
}
func testAgentWithAPIClient(t *testing.T) (*agentWrapper, *api.Client) {
@ -57,6 +57,10 @@ func testAgentWithAPIClient(t *testing.T) (*agentWrapper, *api.Client) {
}
func testAgentWithConfig(t *testing.T, cb func(c *agent.Config)) *agentWrapper {
return testAgentWithConfigReload(t, cb, nil)
}
func testAgentWithConfigReload(t *testing.T, cb func(c *agent.Config), reloadCh chan chan error) *agentWrapper {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("err: %s", err)
@ -66,7 +70,9 @@ func testAgentWithConfig(t *testing.T, cb func(c *agent.Config)) *agentWrapper {
mult := io.MultiWriter(os.Stderr, lw)
conf := nextConfig()
cb(conf)
if cb != nil {
cb(conf)
}
dir, err := ioutil.TempDir("", "agent")
if err != nil {
@ -74,7 +80,7 @@ func testAgentWithConfig(t *testing.T, cb func(c *agent.Config)) *agentWrapper {
}
conf.DataDir = dir
a, err := agent.Create(conf, lw, nil, nil)
a, err := agent.Create(conf, lw, nil, reloadCh)
if err != nil {
os.RemoveAll(dir)
t.Fatalf(fmt.Sprintf("err: %v", err))

View File

@ -1,8 +1,9 @@
package command
import (
"github.com/mitchellh/cli"
"testing"
"github.com/mitchellh/cli"
)
func TestVersionCommand_implements(t *testing.T) {

View File

@ -3,22 +3,21 @@ package command
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"os"
"strconv"
"strings"
"github.com/hashicorp/consul/command/agent"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/watch"
"github.com/mitchellh/cli"
)
// WatchCommand is a Command implementation that is used to setup
// a "watch" which uses a sub-process
type WatchCommand struct {
base.Command
ShutdownCh <-chan struct{}
Ui cli.Ui
}
func (c *WatchCommand) Help() string {
@ -32,49 +31,36 @@ Usage: consul watch [options] [child...]
Providing the watch type is required, and other parameters may be required
or supported depending on the watch type.
Options:
` + c.Command.Help()
-http-addr=127.0.0.1:8500 HTTP address of the Consul agent.
-datacenter="" Datacenter to query. Defaults to that of agent.
-token="" ACL token to use. Defaults to that of agent.
-stale=[true|false] Specifies if watch data is permitted to be stale.
Defaults to false.
Watch Specification:
-key=val Specifies the key to watch. Only for 'key' type.
-name=val Specifies an event name to watch. Only for 'event' type.
-passingonly=[true|false] Specifies if only hosts passing all checks are displayed.
Optional for 'service' type. Defaults false.
-prefix=val Specifies the key prefix to watch. Only for 'keyprefix' type.
-service=val Specifies the service to watch. Required for 'service' type,
optional for 'checks' type.
-state=val Specifies the states to watch. Optional for 'checks' type.
-tag=val Specifies the service tag to filter on. Optional for 'service'
type.
-type=val Specifies the watch type. One of key, keyprefix
services, nodes, service, checks, or event.
`
return strings.TrimSpace(helpText)
}
func (c *WatchCommand) Run(args []string) int {
var watchType, datacenter, token, key, prefix, service, tag, passingOnly, stale, state, name string
cmdFlags := flag.NewFlagSet("watch", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
cmdFlags.StringVar(&watchType, "type", "", "")
cmdFlags.StringVar(&datacenter, "datacenter", "", "")
cmdFlags.StringVar(&token, "token", "", "")
cmdFlags.StringVar(&key, "key", "", "")
cmdFlags.StringVar(&prefix, "prefix", "", "")
cmdFlags.StringVar(&service, "service", "", "")
cmdFlags.StringVar(&tag, "tag", "", "")
cmdFlags.StringVar(&passingOnly, "passingonly", "", "")
cmdFlags.StringVar(&stale, "stale", "", "")
cmdFlags.StringVar(&state, "state", "", "")
cmdFlags.StringVar(&name, "name", "", "")
httpAddr := HTTPAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
var watchType, key, prefix, service, tag, passingOnly, state, name string
f := c.Command.NewFlagSet(c)
f.StringVar(&watchType, "type", "",
"Specifies the watch type. One of key, keyprefix services, nodes, "+
"service, checks, or event.")
f.StringVar(&key, "key", "",
"Specifies the key to watch. Only for 'key' type.")
f.StringVar(&prefix, "prefix", "",
"Specifies the key prefix to watch. Only for 'keyprefix' type.")
f.StringVar(&service, "service", "",
"Specifies the service to watch. Required for 'service' type, "+
"optional for 'checks' type.")
f.StringVar(&tag, "tag", "",
"Specifies the service tag to filter on. Optional for 'service' type.")
f.StringVar(&passingOnly, "passingonly", "",
"Specifies if only hosts passing all checks are displayed. "+
"Optional for 'service' type, must be one of `[true|false]`. Defaults false.")
f.StringVar(&state, "state", "",
"Specifies the states to watch. Optional for 'checks' type.")
f.StringVar(&name, "name", "",
"Specifies an event name to watch. Only for 'event' type.")
if err := c.Command.Parse(args); err != nil {
return 1
}
@ -87,18 +73,18 @@ func (c *WatchCommand) Run(args []string) int {
}
// Grab the script to execute if any
script := strings.Join(cmdFlags.Args(), " ")
script := strings.Join(f.Args(), " ")
// Compile the watch parameters
params := make(map[string]interface{})
if watchType != "" {
params["type"] = watchType
}
if datacenter != "" {
params["datacenter"] = datacenter
if c.Command.HTTPDatacenter() != "" {
params["datacenter"] = c.Command.HTTPDatacenter()
}
if token != "" {
params["token"] = token
if c.Command.HTTPToken() != "" {
params["token"] = c.Command.HTTPToken()
}
if key != "" {
params["key"] = key
@ -112,13 +98,8 @@ func (c *WatchCommand) Run(args []string) int {
if tag != "" {
params["tag"] = tag
}
if stale != "" {
b, err := strconv.ParseBool(stale)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to parse stale flag: %s", err))
return 1
}
params["stale"] = b
if c.Command.HTTPStale() {
params["stale"] = c.Command.HTTPStale()
}
if state != "" {
params["state"] = state
@ -143,7 +124,7 @@ func (c *WatchCommand) Run(args []string) int {
}
// Create and test the HTTP client
client, err := HTTPClient(*httpAddr)
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
@ -213,7 +194,7 @@ func (c *WatchCommand) Run(args []string) int {
}()
// Run the watch
if err := wp.Run(*httpAddr); err != nil {
if err := wp.Run(c.Command.HTTPAddr()); err != nil {
c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
return 1
}

View File

@ -1,9 +1,11 @@
package command
import (
"github.com/mitchellh/cli"
"strings"
"testing"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
func TestWatchCommand_implements(t *testing.T) {
@ -15,7 +17,12 @@ func TestWatchCommandRun(t *testing.T) {
defer a1.Shutdown()
ui := new(cli.MockUi)
c := &WatchCommand{Ui: ui}
c := &WatchCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetHTTP,
},
}
args := []string{"-http-addr=" + a1.httpAddr, "-type=nodes"}
code := c.Run(args)

View File

@ -159,7 +159,10 @@ func init() {
"leave": func() (cli.Command, error) {
return &command.LeaveCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetClientHTTP,
Ui: ui,
},
}, nil
},
@ -175,38 +178,56 @@ func init() {
"maint": func() (cli.Command, error) {
return &command.MaintCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetClientHTTP,
Ui: ui,
},
}, nil
},
"members": func() (cli.Command, error) {
return &command.MembersCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetClientHTTP,
Ui: ui,
},
}, nil
},
"monitor": func() (cli.Command, error) {
return &command.MonitorCommand{
ShutdownCh: makeShutdownCh(),
Ui: ui,
Command: base.Command{
Flags: base.FlagSetClientHTTP,
Ui: ui,
},
}, nil
},
"operator": func() (cli.Command, error) {
return &command.OperatorCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetHTTP,
Ui: ui,
},
}, nil
},
"reload": func() (cli.Command, error) {
return &command.ReloadCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetClientHTTP,
Ui: ui,
},
}, nil
},
"rtt": func() (cli.Command, error) {
return &command.RTTCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetClientHTTP,
Ui: ui,
},
}, nil
},
@ -218,19 +239,28 @@ func init() {
"snapshot restore": func() (cli.Command, error) {
return &command.SnapshotRestoreCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetHTTP,
Ui: ui,
},
}, nil
},
"snapshot save": func() (cli.Command, error) {
return &command.SnapshotSaveCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetHTTP,
Ui: ui,
},
}, nil
},
"snapshot inspect": func() (cli.Command, error) {
return &command.SnapshotInspectCommand{
Ui: ui,
Command: base.Command{
Flags: base.FlagSetNone,
Ui: ui,
},
}, nil
},
@ -244,7 +274,10 @@ func init() {
"watch": func() (cli.Command, error) {
return &command.WatchCommand{
ShutdownCh: makeShutdownCh(),
Ui: ui,
Command: base.Command{
Flags: base.FlagSetHTTP,
Ui: ui,
},
}, nil
},
}

View File

@ -21,12 +21,8 @@ non-graceful leave can affect cluster availability.
## Usage
Usage: `consul leave`
Usage: `consul leave [options]`
The command-line flags are all optional. The list of available flags are:
* `-rpc-addr` - Address to the RPC server of the agent you want to contact
to send this command. If this isn't specified, the command checks the
CONSUL_RPC_ADDR env variable. If this isn't set, the default RPC
address will be set to "127.0.0.1:8400".
#### API Options
<%= partial "docs/commands/http_api_options_client" %>

View File

@ -24,9 +24,11 @@ health check.
Usage: `consul maint [options]`
All of the command line arguments are optional.
#### API Options
The list of available flags are:
<%= partial "docs/commands/http_api_options_client" %>
#### Command Options
* `-enable` - Enable maintenance mode on a given service or node. If
combined with the `-service` flag, we operate on a specific service ID.
@ -44,12 +46,6 @@ The list of available flags are:
providing this flag, the `-enable` and `-disable` flags functionality is
modified to operate on the given service ID.
* `-token` - ACL token to use. Defaults to that of agent.
* `-http-addr` - Address to the HTTP server of the agent you want to contact
to send this command. If this isn't specified, the command will contact
"127.0.0.1:8500" which is the default HTTP address of a Consul agent.
## List mode
If neither `-enable` nor `-disable` are passed, the `maint` command will

View File

@ -22,16 +22,15 @@ that the failure is actually just a network partition.
Usage: `consul members [options]`
The command-line flags are all optional. The list of available flags are:
#### API Options
<%= partial "docs/commands/http_api_options_client" %>
#### Command Options
* `-detailed` - If provided, output shows more detailed information
about each node.
* `-rpc-addr` - Address to the RPC server of the agent you want to contact
to send this command.If this isn't specified, the command checks the
CONSUL_RPC_ADDR env variable. If this isn't set, the default RPC
address will be set to "127.0.0.1:8400".
* `-status` - If provided, output is filtered to only nodes matching
the regular expression for status

View File

@ -22,14 +22,13 @@ logs and watch the debug logs if necessary.
Usage: `consul monitor [options]`
The command-line flags are all optional. The list of available flags are:
#### API Options
<%= partial "docs/commands/http_api_options_client" %>
#### Command Options
* `-log-level` - The log level of the messages to show. By default this
is "info". This log level can be more verbose than what the agent is
configured to run at. Available log levels are "trace", "debug", "info",
"warn", and "err".
* `-rpc-addr` - Address to the RPC server of the agent you want to contact
to send this command. If this isn't specified, the command checks the
CONSUL_RPC_ADDR env variable. If this isn't set, the default RPC
address will be set to "127.0.0.1:8400".

View File

@ -28,20 +28,17 @@ endpoint.
## Usage
Usage: `consul operator <subcommand> [common options] [action] [options]`
Usage: `consul operator <subcommand> [action] [options]`
Run `consul operator <subcommand>` with no arguments for help on that
subcommand. The following subcommands are available:
* `raft` - View and modify Consul's Raft configuration.
Options common to all subcommands include:
#### API Options
* `-http-addr` - Address to the HTTP server of the agent you want to contact
to send this command. If this isn't specified, the command will contact
"127.0.0.1:8500" which is the default HTTP address of a Consul agent.
* `-token` - ACL token to use. Defaults to that of agent.
<%= partial "docs/commands/http_api_options_client" %>
<%= partial "docs/commands/http_api_options_server" %>
## Raft Operations

View File

@ -29,10 +29,6 @@ section on the agent options page for details on which options are supported.
Usage: `consul reload`
The command-line flags are all optional. The list of available flags are:
* `-rpc-addr` - Address to the RPC server of the agent you want to contact
to send this command. If this isn't specified, the command checks the
CONSUL_RPC_ADDR env variable. If this isn't set, the default RPC
address will be set to "127.0.0.1:8400".
#### API Options
<%= partial "docs/commands/http_api_options_client" %>

View File

@ -24,7 +24,11 @@ At least one node name is required. If the second node name isn't given, it
is set to the agent's node name. These are the node names as known to
Consul as the `consul members` command would show, not IP addresses.
The list of available flags are:
#### API Options
<%= partial "docs/commands/http_api_options_client" %>
#### Command Options
* `-wan` - Instructs the command to use WAN coordinates instead of LAN
coordinates. By default, the two nodes are assumed to be nodes in the local
@ -33,11 +37,6 @@ The list of available flags are:
and the datacenter (eg. "myserver.dc1"). It is not possible to measure between
LAN coordinates and WAN coordinates, so both nodes must be in the same pool.
* `-http-addr` - Address to the HTTP server of the agent you want to contact
to send this command. If this isn't specified, the command will contact
"127.0.0.1:8500" which is the default HTTP address of a Consul agent.
The following environment variables control accessing the HTTP server via SSL:
* `CONSUL_HTTP_SSL` Set this to enable SSL

View File

@ -28,6 +28,7 @@ Usage: `consul snapshot restore [options] FILE`
#### API Options
<%= partial "docs/commands/http_api_options_client" %>
<%= partial "docs/commands/http_api_options_server" %>
## Examples

View File

@ -23,6 +23,7 @@ Usage: `consul snapshot save [options] FILE`
#### API Options
<%= partial "docs/commands/http_api_options_client" %>
<%= partial "docs/commands/http_api_options_server" %>
## Examples
@ -41,7 +42,7 @@ After the snapshot is written to the given file it is read back and verified for
integrity.
To create a potentially stale snapshot from any available server, use the stale
consisentcy mode:
consistency mode:
```text
$ consul snapshot save -stale backup.snap

View File

@ -0,0 +1,20 @@
---
layout: "docs"
page_title: "Commands: Version"
sidebar_current: "docs-commands-version"
description: |-
The `version` command prints the version of Consul and the protocol versions it understands for speaking to other agents.
---
# Consul Version
Command: `consul version`
The `version` command prints the version of Consul and the protocol versions it understands for speaking to other agents.
```text
$ consul version
Consul v0.7.4
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
```

View File

@ -27,18 +27,12 @@ data view. Depending on the type, various options may be required
or optionally provided. There is more documentation on watch
[specifications here](/docs/agent/watches.html).
The list of available flags are:
#### API Options
* `-http-addr` - Address to the HTTP server of the agent you want to contact
to send this command. If this isn't specified, the command will contact
"127.0.0.1:8500" which is the default HTTP address of a Consul agent.
<%= partial "docs/commands/http_api_options_client" %>
<%= partial "docs/commands/http_api_options_server" %>
* `-datacenter` - Datacenter to query. Defaults to that of the agent.
* `-token` - ACL token to use. Defaults to that of the agent.
* `-stale=[true|false]` - Specifies if watch data is permitted to be stale. Defaults
to false.
#### Command Options
* `-key` - Key to watch. Only for `key` type.

View File

@ -173,6 +173,10 @@
</ul>
</li>
<li<%= sidebar_current("docs-commands-version") %>>
<a href="/docs/commands/version.html">version</a>
</li>
<li<%= sidebar_current("docs-commands-watch") %>>
<a href="/docs/commands/watch.html">watch</a>
</li>