Convert operator command to use base.Command

This commit is contained in:
Kyle Havlovitz 2017-02-09 18:19:34 -05:00
parent be17779c42
commit 369b4b6d73
No known key found for this signature in database
GPG Key ID: 8A5E6B173056AD6C
6 changed files with 60 additions and 50 deletions

View File

@ -58,6 +58,12 @@ func (c *Command) HTTPClient() (*api.Client, error) {
return api.NewClient(config) 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. // httpFlagsClient is the list of flags that apply to HTTP connections.
func (c *Command) httpFlagsClient(f *flag.FlagSet) *flag.FlagSet { func (c *Command) httpFlagsClient(f *flag.FlagSet) *flag.FlagSet {
if f == nil { if f == nil {

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

@ -164,7 +164,10 @@ func init() {
"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
}, },

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