Convert exec command to use base.Command

This commit is contained in:
Kyle Havlovitz 2017-02-08 16:57:46 -05:00
parent 6cc2299123
commit 89771b6075
No known key found for this signature in database
GPG key ID: 8A5E6B173056AD6C
3 changed files with 52 additions and 75 deletions

View file

@ -3,7 +3,6 @@ package command
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"os"
@ -15,6 +14,7 @@ import (
"unicode"
consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/base"
"github.com/mitchellh/cli"
)
@ -53,9 +53,7 @@ const (
// rExecConf is used to pass around configuration
type rExecConf struct {
datacenter string
prefix string
token string
prefix string
foreignDC bool
localDC string
@ -118,8 +116,9 @@ type rExecExit struct {
// ExecCommand is a Command implementation that is used to
// do remote execution of commands
type ExecCommand struct {
base.Command
ShutdownCh <-chan struct{}
Ui cli.Ui
conf rExecConf
client *consulapi.Client
sessionID string
@ -127,24 +126,29 @@ type ExecCommand struct {
}
func (c *ExecCommand) Run(args []string) int {
cmdFlags := flag.NewFlagSet("exec", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
cmdFlags.StringVar(&c.conf.datacenter, "datacenter", "", "")
cmdFlags.StringVar(&c.conf.node, "node", "", "")
cmdFlags.StringVar(&c.conf.service, "service", "", "")
cmdFlags.StringVar(&c.conf.tag, "tag", "", "")
cmdFlags.StringVar(&c.conf.prefix, "prefix", rExecPrefix, "")
cmdFlags.DurationVar(&c.conf.replWait, "wait-repl", rExecReplicationWait, "")
cmdFlags.DurationVar(&c.conf.wait, "wait", rExecQuietWait, "")
cmdFlags.BoolVar(&c.conf.verbose, "verbose", false, "")
cmdFlags.StringVar(&c.conf.token, "token", "", "")
httpAddr := HTTPAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil {
f := c.Command.NewFlagSet(c)
f.StringVar(&c.conf.node, "node", "",
"Regular expression to filter on node names.")
f.StringVar(&c.conf.service, "service", "",
"Regular expression to filter on service instances.")
f.StringVar(&c.conf.tag, "tag", "",
"Regular expression to filter on service tags. Must be used with -service.")
f.StringVar(&c.conf.prefix, "prefix", rExecPrefix,
"Prefix in the KV store to use for request data.")
f.DurationVar(&c.conf.wait, "wait", rExecQuietWait,
"Period to wait with no responses before terminating execution.")
f.DurationVar(&c.conf.replWait, "wait-repl", rExecReplicationWait,
"Period to wait for replication before firing event. This is an "+
"optimization to allow stale reads to be performed.")
f.BoolVar(&c.conf.verbose, "verbose", false,
"Enables verbose output.")
if err := c.Command.Parse(args); err != nil {
return 1
}
// Join the commands to execute
c.conf.cmd = strings.Join(cmdFlags.Args(), " ")
c.conf.cmd = strings.Join(f.Args(), " ")
// If there is no command, read stdin for a script input
if c.conf.cmd == "-" {
@ -175,11 +179,7 @@ func (c *ExecCommand) Run(args []string) int {
}
// Create and test the HTTP client
client, err := HTTPClientConfig(func(clientConf *consulapi.Config) {
clientConf.Address = *httpAddr
clientConf.Datacenter = c.conf.datacenter
clientConf.Token = c.conf.token
})
client, err := c.Command.HTTPClient()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
@ -192,7 +192,7 @@ func (c *ExecCommand) Run(args []string) int {
c.client = client
// Check if this is a foreign datacenter
if c.conf.datacenter != "" && c.conf.datacenter != info["Config"]["Datacenter"] {
if c.Command.HTTPDatacenter() != "" && c.Command.HTTPDatacenter() != info["Config"]["Datacenter"] {
if c.conf.verbose {
c.Ui.Info("Remote exec in foreign datacenter, using Session TTL")
}
@ -489,7 +489,7 @@ func (c *ExecCommand) createSessionForeign() (string, error) {
node := services[0].Node.Node
if c.conf.verbose {
c.Ui.Info(fmt.Sprintf("Binding session to remote node %s@%s",
node, c.conf.datacenter))
node, c.Command.HTTPDatacenter()))
}
session := c.client.Session()
@ -618,22 +618,8 @@ Usage: consul exec [options] [-|command...]
definitions. If a command is '-', stdin will be read until EOF
and used as a script input.
Options:
` + c.Command.Help()
-http-addr=127.0.0.1:8500 HTTP address of the Consul agent.
-datacenter="" Datacenter to dispatch in. Defaults to that of agent.
-prefix="_rexec" Prefix in the KV store to use for request data
-node="" Regular expression to filter on node names
-service="" Regular expression to filter on service instances
-tag="" Regular expression to filter on service tags. Must be used
with -service.
-wait=2s Period to wait with no responses before terminating execution.
-wait-repl=200ms Period to wait for replication before firing event. This is an
optimization to allow stale reads to be performed.
-verbose Enables verbose output
-token="" ACL token to use during requests. Defaults to that
of the agent.
`
return strings.TrimSpace(helpText)
}

View file

@ -8,10 +8,21 @@ import (
consulapi "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/agent"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/testutil"
"github.com/mitchellh/cli"
)
func testExecCommand(t *testing.T) (*cli.MockUi, *ExecCommand) {
ui := new(cli.MockUi)
return ui, &ExecCommand{
Command: base.Command{
Ui: ui,
Flags: base.FlagSetHTTP,
},
}
}
func TestExecCommand_implements(t *testing.T) {
var _ cli.Command = &ExecCommand{}
}
@ -21,8 +32,7 @@ func TestExecCommandRun(t *testing.T) {
defer a1.Shutdown()
waitForLeader(t, a1.httpAddr)
ui := new(cli.MockUi)
c := &ExecCommand{Ui: ui}
ui, c := testExecCommand(t)
args := []string{"-http-addr=" + a1.httpAddr, "-wait=10s", "uptime"}
code := c.Run(args)
@ -57,8 +67,7 @@ func TestExecCommandRun_CrossDC(t *testing.T) {
waitForLeader(t, a1.httpAddr)
waitForLeader(t, a2.httpAddr)
ui := new(cli.MockUi)
c := &ExecCommand{Ui: ui}
ui, c := testExecCommand(t)
args := []string{"-http-addr=" + a1.httpAddr,
"-wait=400ms", "-datacenter=dc2", "uptime"}
@ -130,11 +139,8 @@ func TestExecCommand_Sessions(t *testing.T) {
t.Fatalf("err: %v", err)
}
ui := new(cli.MockUi)
c := &ExecCommand{
Ui: ui,
client: client,
}
_, c := testExecCommand(t)
c.client = client
id, err := c.createSession()
if err != nil {
@ -174,11 +180,8 @@ func TestExecCommand_Sessions_Foreign(t *testing.T) {
t.Fatalf("err: %v", err)
}
ui := new(cli.MockUi)
c := &ExecCommand{
Ui: ui,
client: client,
}
_, c := testExecCommand(t)
c.client = client
c.conf.foreignDC = true
c.conf.localDC = "dc1"
@ -228,11 +231,8 @@ func TestExecCommand_UploadDestroy(t *testing.T) {
t.Fatalf("err: %v", err)
}
ui := new(cli.MockUi)
c := &ExecCommand{
Ui: ui,
client: client,
}
_, c := testExecCommand(t)
c.client = client
id, err := c.createSession()
if err != nil {
@ -288,11 +288,8 @@ func TestExecCommand_StreamResults(t *testing.T) {
t.Fatalf("err: %v", err)
}
ui := new(cli.MockUi)
c := &ExecCommand{
Ui: ui,
client: client,
}
_, c := testExecCommand(t)
c.client = client
c.conf.prefix = "_rexec"
id, err := c.createSession()

View file

@ -37,14 +37,12 @@ The only required option is a command to execute. This is either given
as trailing arguments, or by specifying `-`; STDIN will be read to
completion as a script to evaluate.
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 agent. In version
0.4, that is the only supported value.
#### Command Options
* `-prefix` - Key prefix in the KV store to use for storing request data.
Defaults to `_rexec`.
@ -67,7 +65,3 @@ The list of available flags are:
to 200 msec.
* `-verbose` - Enables verbose output.
* `-token` - The ACL token to use during requests. This token must have access
to the prefix in the KV store as well as exec "write" access for the `_rexec`
event. Defaults to that of the agent.