diff --git a/command/base/command.go b/command/base/command.go index 3d4020924f..87cc37ba4a 100644 --- a/command/base/command.go +++ b/command/base/command.go @@ -58,6 +58,12 @@ func (c *Command) HTTPClient() (*api.Client, error) { return api.NewClient(config) } +func (c *Command) HTTPStale() bool { + var stale bool + c.stale.Merge(&stale) + return stale +} + // httpFlagsClient is the list of flags that apply to HTTP connections. func (c *Command) httpFlagsClient(f *flag.FlagSet) *flag.FlagSet { if f == nil { diff --git a/command/monitor.go b/command/monitor.go index 4460b66d49..07aded7f3b 100644 --- a/command/monitor.go +++ b/command/monitor.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" "sync" - + "github.com/hashicorp/consul/command/base" ) diff --git a/command/operator.go b/command/operator.go index b049e325a9..cf16040b7a 100644 --- a/command/operator.go +++ b/command/operator.go @@ -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 [common options] [action] [options] +Usage: consul operator [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 [common options] [action] [options] Run consul operator 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 diff --git a/command/operator_test.go b/command/operator_test.go index e65434b75d..d31cac0074 100644 --- a/command/operator_test.go +++ b/command/operator_test.go @@ -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) diff --git a/commands.go b/commands.go index 30aebced64..396ba92336 100644 --- a/commands.go +++ b/commands.go @@ -164,7 +164,10 @@ func init() { "operator": func() (cli.Command, error) { return &command.OperatorCommand{ - Ui: ui, + Command: base.Command{ + Flags: base.FlagSetHTTP, + Ui: ui, + }, }, nil }, diff --git a/website/source/docs/commands/operator.html.markdown b/website/source/docs/commands/operator.html.markdown.erb similarity index 89% rename from website/source/docs/commands/operator.html.markdown rename to website/source/docs/commands/operator.html.markdown.erb index 24001366d7..a25eb69a0c 100644 --- a/website/source/docs/commands/operator.html.markdown +++ b/website/source/docs/commands/operator.html.markdown.erb @@ -28,20 +28,17 @@ endpoint. ## Usage -Usage: `consul operator [common options] [action] [options]` +Usage: `consul operator [action] [options]` Run `consul operator ` 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