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 93853340f0
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) 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 { func (c *Command) HTTPDatacenter() string {
return c.datacenter.String() return c.datacenter.String()
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,24 +1,24 @@
package command package command
import ( import (
"flag"
"fmt" "fmt"
"strings" "strings"
"flag"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
"github.com/mitchellh/cli" "github.com/hashicorp/consul/command/base"
"github.com/ryanuber/columnize" "github.com/ryanuber/columnize"
) )
// OperatorCommand is used to provide various low-level tools for Consul // OperatorCommand is used to provide various low-level tools for Consul
// operators. // operators.
type OperatorCommand struct { type OperatorCommand struct {
Ui cli.Ui base.Command
} }
func (c *OperatorCommand) Help() string { func (c *OperatorCommand) Help() string {
helpText := ` 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 Provides cluster-level tools for Consul operators, such as interacting with
the Raft subsystem. NOTE: Use this command with extreme caution, as improper 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 Run consul operator <subcommand> with no arguments for help on that
subcommand. 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: Subcommands:
raft View and modify Consul's Raft configuration. raft View and modify Consul's Raft configuration.
@ -73,17 +68,15 @@ func (c *OperatorCommand) Synopsis() string {
} }
const raftHelp = ` 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. Displays the current Raft peer configuration.
The -stale argument defaults to "false" which means the leader provides the consul operator raft -remove-peer -address="IP:port"
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"
Removes Consul server with given -address from the Raft configuration. 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 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 members" command, it is preferable to clean up by simply running
"consul force-leave" instead of this command. "consul force-leave" instead of this command.
` `
// raft handles the raft subcommands. // raft handles the raft subcommands.
func (c *OperatorCommand) raft(args []string) error { func (c *OperatorCommand) raft(args []string) error {
cmdFlags := flag.NewFlagSet("raft", flag.ContinueOnError) f := c.Command.NewFlagSet(c)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
// Parse verb arguments. // Parse verb arguments.
var listPeers, removePeer bool var listPeers, removePeer bool
cmdFlags.BoolVar(&listPeers, "list-peers", false, "") f.BoolVar(&listPeers, "list-peers", false,
cmdFlags.BoolVar(&removePeer, "remove-peer", 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. // Parse other arguments.
var stale bool var address string
var address, token string f.StringVar(&address, "address", "",
cmdFlags.StringVar(&address, "address", "", "") "The address to remove from the Raft configuration.")
cmdFlags.BoolVar(&stale, "stale", false, "")
cmdFlags.StringVar(&token, "token", "", "") if err := c.Command.Parse(args); err != nil {
httpAddr := HTTPAddrFlag(cmdFlags) if err == flag.ErrHelp {
if err := cmdFlags.Parse(args); err != nil { c.Ui.Output("")
c.Ui.Output(strings.TrimSpace(raftHelp + c.Command.Help()))
return nil
}
return err return err
} }
// Set up a client. // Set up a client.
conf := api.DefaultConfig() client, err := c.Command.HTTPClient()
conf.Address = *httpAddr
client, err := api.NewClient(conf)
if err != nil { if err != nil {
return fmt.Errorf("error connecting to Consul agent: %s", err) return fmt.Errorf("error connecting to Consul agent: %s", err)
} }
@ -129,8 +128,7 @@ func (c *OperatorCommand) raft(args []string) error {
if listPeers { if listPeers {
// Fetch the current configuration. // Fetch the current configuration.
q := &api.QueryOptions{ q := &api.QueryOptions{
AllowStale: stale, AllowStale: c.Command.HTTPStale(),
Token: token,
} }
reply, err := operator.RaftGetConfiguration(q) reply, err := operator.RaftGetConfiguration(q)
if err != nil { if err != nil {
@ -156,17 +154,14 @@ func (c *OperatorCommand) raft(args []string) error {
} }
// Try to kick the peer. // Try to kick the peer.
w := &api.WriteOptions{ if err := operator.RaftRemovePeerByAddress(address, nil); err != nil {
Token: token,
}
if err := operator.RaftRemovePeerByAddress(address, w); err != nil {
return err return err
} }
c.Ui.Output(fmt.Sprintf("Removed peer with address %q", address)) c.Ui.Output(fmt.Sprintf("Removed peer with address %q", address))
} else { } else {
c.Ui.Output(c.Help()) c.Ui.Output(c.Help())
c.Ui.Output("") c.Ui.Output("")
c.Ui.Output(strings.TrimSpace(raftHelp)) c.Ui.Output(strings.TrimSpace(raftHelp + c.Command.Help()))
} }
return nil return nil

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ func (a *agentWrapper) Shutdown() {
} }
func testAgent(t *testing.T) *agentWrapper { 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) { 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 { 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") l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
t.Fatalf("err: %s", err) 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) mult := io.MultiWriter(os.Stderr, lw)
conf := nextConfig() conf := nextConfig()
cb(conf) if cb != nil {
cb(conf)
}
dir, err := ioutil.TempDir("", "agent") dir, err := ioutil.TempDir("", "agent")
if err != nil { if err != nil {
@ -74,7 +80,7 @@ func testAgentWithConfig(t *testing.T, cb func(c *agent.Config)) *agentWrapper {
} }
conf.DataDir = dir conf.DataDir = dir
a, err := agent.Create(conf, lw, nil, nil) a, err := agent.Create(conf, lw, nil, reloadCh)
if err != nil { if err != nil {
os.RemoveAll(dir) os.RemoveAll(dir)
t.Fatalf(fmt.Sprintf("err: %v", err)) t.Fatalf(fmt.Sprintf("err: %v", err))

View File

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

View File

@ -3,22 +3,21 @@ package command
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"flag"
"fmt" "fmt"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"github.com/hashicorp/consul/command/agent" "github.com/hashicorp/consul/command/agent"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/watch" "github.com/hashicorp/consul/watch"
"github.com/mitchellh/cli"
) )
// WatchCommand is a Command implementation that is used to setup // WatchCommand is a Command implementation that is used to setup
// a "watch" which uses a sub-process // a "watch" which uses a sub-process
type WatchCommand struct { type WatchCommand struct {
base.Command
ShutdownCh <-chan struct{} ShutdownCh <-chan struct{}
Ui cli.Ui
} }
func (c *WatchCommand) Help() string { 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 Providing the watch type is required, and other parameters may be required
or supported depending on the watch type. 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) return strings.TrimSpace(helpText)
} }
func (c *WatchCommand) Run(args []string) int { func (c *WatchCommand) Run(args []string) int {
var watchType, datacenter, token, key, prefix, service, tag, passingOnly, stale, state, name string var watchType, key, prefix, service, tag, passingOnly, state, name string
cmdFlags := flag.NewFlagSet("watch", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) } f := c.Command.NewFlagSet(c)
cmdFlags.StringVar(&watchType, "type", "", "") f.StringVar(&watchType, "type", "",
cmdFlags.StringVar(&datacenter, "datacenter", "", "") "Specifies the watch type. One of key, keyprefix services, nodes, "+
cmdFlags.StringVar(&token, "token", "", "") "service, checks, or event.")
cmdFlags.StringVar(&key, "key", "", "") f.StringVar(&key, "key", "",
cmdFlags.StringVar(&prefix, "prefix", "", "") "Specifies the key to watch. Only for 'key' type.")
cmdFlags.StringVar(&service, "service", "", "") f.StringVar(&prefix, "prefix", "",
cmdFlags.StringVar(&tag, "tag", "", "") "Specifies the key prefix to watch. Only for 'keyprefix' type.")
cmdFlags.StringVar(&passingOnly, "passingonly", "", "") f.StringVar(&service, "service", "",
cmdFlags.StringVar(&stale, "stale", "", "") "Specifies the service to watch. Required for 'service' type, "+
cmdFlags.StringVar(&state, "state", "", "") "optional for 'checks' type.")
cmdFlags.StringVar(&name, "name", "", "") f.StringVar(&tag, "tag", "",
httpAddr := HTTPAddrFlag(cmdFlags) "Specifies the service tag to filter on. Optional for 'service' type.")
if err := cmdFlags.Parse(args); err != nil { 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 return 1
} }
@ -87,18 +73,18 @@ func (c *WatchCommand) Run(args []string) int {
} }
// Grab the script to execute if any // Grab the script to execute if any
script := strings.Join(cmdFlags.Args(), " ") script := strings.Join(f.Args(), " ")
// Compile the watch parameters // Compile the watch parameters
params := make(map[string]interface{}) params := make(map[string]interface{})
if watchType != "" { if watchType != "" {
params["type"] = watchType params["type"] = watchType
} }
if datacenter != "" { if c.Command.HTTPDatacenter() != "" {
params["datacenter"] = datacenter params["datacenter"] = c.Command.HTTPDatacenter()
} }
if token != "" { if c.Command.HTTPToken() != "" {
params["token"] = token params["token"] = c.Command.HTTPToken()
} }
if key != "" { if key != "" {
params["key"] = key params["key"] = key
@ -112,13 +98,8 @@ func (c *WatchCommand) Run(args []string) int {
if tag != "" { if tag != "" {
params["tag"] = tag params["tag"] = tag
} }
if stale != "" { if c.Command.HTTPStale() {
b, err := strconv.ParseBool(stale) params["stale"] = c.Command.HTTPStale()
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to parse stale flag: %s", err))
return 1
}
params["stale"] = b
} }
if state != "" { if state != "" {
params["state"] = state params["state"] = state
@ -143,7 +124,7 @@ func (c *WatchCommand) Run(args []string) int {
} }
// Create and test the HTTP client // Create and test the HTTP client
client, err := HTTPClient(*httpAddr) client, err := c.Command.HTTPClient()
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
@ -213,7 +194,7 @@ func (c *WatchCommand) Run(args []string) int {
}() }()
// Run the watch // 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)) c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
return 1 return 1
} }

View File

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

View File

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

View File

@ -21,12 +21,8 @@ non-graceful leave can affect cluster availability.
## Usage ## Usage
Usage: `consul leave` Usage: `consul leave [options]`
The command-line flags are all optional. The list of available flags are: #### API Options
* `-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".
<%= partial "docs/commands/http_api_options_client" %>

View File

@ -24,9 +24,11 @@ health check.
Usage: `consul maint [options]` 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 * `-enable` - Enable maintenance mode on a given service or node. If
combined with the `-service` flag, we operate on a specific service ID. 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 providing this flag, the `-enable` and `-disable` flags functionality is
modified to operate on the given service ID. 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 ## List mode
If neither `-enable` nor `-disable` are passed, the `maint` command will 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]` 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 * `-detailed` - If provided, output shows more detailed information
about each node. 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 * `-status` - If provided, output is filtered to only nodes matching
the regular expression for status the regular expression for status

View File

@ -22,14 +22,13 @@ logs and watch the debug logs if necessary.
Usage: `consul monitor [options]` 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 * `-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 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", configured to run at. Available log levels are "trace", "debug", "info",
"warn", and "err". "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
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 Run `consul operator <subcommand>` with no arguments for help on that
subcommand. The following subcommands are available: subcommand. The following subcommands are available:
* `raft` - View and modify Consul's Raft configuration. * `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 <%= partial "docs/commands/http_api_options_client" %>
to send this command. If this isn't specified, the command will contact <%= partial "docs/commands/http_api_options_server" %>
"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.
## Raft Operations ## Raft Operations

View File

@ -29,10 +29,6 @@ section on the agent options page for details on which options are supported.
Usage: `consul reload` Usage: `consul reload`
The command-line flags are all optional. The list of available flags are: #### API Options
* `-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".
<%= 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 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. 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 * `-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 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 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. 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: The following environment variables control accessing the HTTP server via SSL:
* `CONSUL_HTTP_SSL` Set this to enable SSL * `CONSUL_HTTP_SSL` Set this to enable SSL

View File

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

View File

@ -23,6 +23,7 @@ Usage: `consul snapshot save [options] FILE`
#### API Options #### API Options
<%= partial "docs/commands/http_api_options_client" %> <%= partial "docs/commands/http_api_options_client" %>
<%= partial "docs/commands/http_api_options_server" %>
## Examples ## Examples
@ -41,7 +42,7 @@ After the snapshot is written to the given file it is read back and verified for
integrity. integrity.
To create a potentially stale snapshot from any available server, use the stale To create a potentially stale snapshot from any available server, use the stale
consisentcy mode: consistency mode:
```text ```text
$ consul snapshot save -stale backup.snap $ 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 or optionally provided. There is more documentation on watch
[specifications here](/docs/agent/watches.html). [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 <%= partial "docs/commands/http_api_options_client" %>
to send this command. If this isn't specified, the command will contact <%= partial "docs/commands/http_api_options_server" %>
"127.0.0.1:8500" which is the default HTTP address of a Consul agent.
* `-datacenter` - Datacenter to query. Defaults to that of the agent. #### Command Options
* `-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.
* `-key` - Key to watch. Only for `key` type. * `-key` - Key to watch. Only for `key` type.

View File

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