mirror of https://github.com/status-im/consul.git
Move command Meta to base.Command and split http options
This commit is contained in:
parent
edc353914d
commit
49d2ce1c3d
|
@ -1,4 +1,4 @@
|
||||||
package command
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
@ -21,11 +21,14 @@ const maxLineLength int = 72
|
||||||
type FlagSetFlags uint
|
type FlagSetFlags uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FlagSetNone FlagSetFlags = iota << 1
|
FlagSetNone FlagSetFlags = iota << 1
|
||||||
FlagSetHTTP FlagSetFlags = iota << 1
|
FlagSetClientHTTP FlagSetFlags = iota << 1
|
||||||
|
FlagSetServerHTTP FlagSetFlags = iota << 1
|
||||||
|
|
||||||
|
FlagSetHTTP = FlagSetClientHTTP | FlagSetServerHTTP
|
||||||
)
|
)
|
||||||
|
|
||||||
type Meta struct {
|
type Command struct {
|
||||||
Ui cli.Ui
|
Ui cli.Ui
|
||||||
Flags FlagSetFlags
|
Flags FlagSetFlags
|
||||||
|
|
||||||
|
@ -33,47 +36,64 @@ type Meta struct {
|
||||||
|
|
||||||
// These are the options which correspond to the HTTP API options
|
// These are the options which correspond to the HTTP API options
|
||||||
httpAddr string
|
httpAddr string
|
||||||
datacenter string
|
|
||||||
token string
|
token string
|
||||||
|
datacenter string
|
||||||
stale bool
|
stale bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPClient returns a client with the parsed flags. It panics if the command
|
// HTTPClient returns a client with the parsed flags. It panics if the command
|
||||||
// does not accept HTTP flags or if the flags have not been parsed.
|
// does not accept HTTP flags or if the flags have not been parsed.
|
||||||
func (m *Meta) HTTPClient() (*api.Client, error) {
|
func (c *Command) HTTPClient() (*api.Client, error) {
|
||||||
if !m.hasHTTP() {
|
if !c.hasClientHTTP() && !c.hasServerHTTP() {
|
||||||
panic("no http flags defined")
|
panic("no http flags defined")
|
||||||
}
|
}
|
||||||
if !m.flagSet.Parsed() {
|
if !c.flagSet.Parsed() {
|
||||||
panic("flags have not been parsed")
|
panic("flags have not been parsed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.NewClient(&api.Config{
|
config := api.DefaultConfig()
|
||||||
Datacenter: m.datacenter,
|
if c.datacenter != "" {
|
||||||
Address: m.httpAddr,
|
config.Datacenter = c.datacenter
|
||||||
Token: m.token,
|
}
|
||||||
})
|
if c.httpAddr != "" {
|
||||||
|
config.Address = c.httpAddr
|
||||||
|
}
|
||||||
|
if c.token != "" {
|
||||||
|
config.Token = c.token
|
||||||
|
}
|
||||||
|
c.Ui.Info(fmt.Sprintf("client http addr: %s", config.Address))
|
||||||
|
return api.NewClient(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpFlags is the list of flags that apply to HTTP connections.
|
// httpFlagsClient is the list of flags that apply to HTTP connections.
|
||||||
func (m *Meta) httpFlags(f *flag.FlagSet) *flag.FlagSet {
|
func (c *Command) httpFlagsClient(f *flag.FlagSet) *flag.FlagSet {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = flag.NewFlagSet("", flag.ContinueOnError)
|
f = flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.StringVar(&m.datacenter, "datacenter", "",
|
f.StringVar(&c.httpAddr, "http-addr", "",
|
||||||
"Name of the datacenter to query. If unspecified, this will default to "+
|
|
||||||
"the datacenter of the queried agent.")
|
|
||||||
f.StringVar(&m.httpAddr, "http-addr", "",
|
|
||||||
"Address and port to the Consul HTTP agent. The value can be an IP "+
|
"Address and port to the Consul HTTP agent. The value can be an IP "+
|
||||||
"address or DNS address, but it must also include the port. This can "+
|
"address or DNS address, but it must also include the port. This can "+
|
||||||
"also be specified via the CONSUL_HTTP_ADDR environment variable. The "+
|
"also be specified via the CONSUL_HTTP_ADDR environment variable. The "+
|
||||||
"default value is 127.0.0.1:8500.")
|
"default value is 127.0.0.1:8500.")
|
||||||
f.StringVar(&m.token, "token", "",
|
f.StringVar(&c.token, "token", "",
|
||||||
"ACL token to use in the request. This can also be specified via the "+
|
"ACL token to use in the request. This can also be specified via the "+
|
||||||
"CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+
|
"CONSUL_HTTP_TOKEN environment variable. If unspecified, the query will "+
|
||||||
"default to the token of the Consul agent at the HTTP address.")
|
"default to the token of the Consul agent at the HTTP address.")
|
||||||
f.BoolVar(&m.stale, "stale", false,
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpFlagsServer is the list of flags that apply to HTTP connections.
|
||||||
|
func (c *Command) httpFlagsServer(f *flag.FlagSet) *flag.FlagSet {
|
||||||
|
if f == nil {
|
||||||
|
f = flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.StringVar(&c.datacenter, "datacenter", "",
|
||||||
|
"Name of the datacenter to query. If unspecified, this will default to "+
|
||||||
|
"the datacenter of the queried agent.")
|
||||||
|
f.BoolVar(&c.stale, "stale", false,
|
||||||
"Permit any Consul server (non-leader) to respond to this request. This "+
|
"Permit any Consul server (non-leader) to respond to this request. This "+
|
||||||
"allows for lower latency and higher throughput, but can result in "+
|
"allows for lower latency and higher throughput, but can result in "+
|
||||||
"stale data. This option has no effect on non-read operations. The "+
|
"stale data. This option has no effect on non-read operations. The "+
|
||||||
|
@ -84,71 +104,94 @@ func (m *Meta) httpFlags(f *flag.FlagSet) *flag.FlagSet {
|
||||||
|
|
||||||
// NewFlagSet creates a new flag set for the given command. It automatically
|
// NewFlagSet creates a new flag set for the given command. It automatically
|
||||||
// generates help output and adds the appropriate API flags.
|
// generates help output and adds the appropriate API flags.
|
||||||
func (m *Meta) NewFlagSet(c cli.Command) *flag.FlagSet {
|
func (c *Command) NewFlagSet(command cli.Command) *flag.FlagSet {
|
||||||
f := flag.NewFlagSet("", flag.ContinueOnError)
|
f := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
f.Usage = func() { m.Ui.Error(c.Help()) }
|
f.Usage = func() { c.Ui.Error(command.Help()) }
|
||||||
|
|
||||||
if m.hasHTTP() {
|
if c.hasClientHTTP() {
|
||||||
m.httpFlags(f)
|
c.httpFlagsClient(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.hasServerHTTP() {
|
||||||
|
c.httpFlagsServer(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
errR, errW := io.Pipe()
|
errR, errW := io.Pipe()
|
||||||
errScanner := bufio.NewScanner(errR)
|
errScanner := bufio.NewScanner(errR)
|
||||||
go func() {
|
go func() {
|
||||||
for errScanner.Scan() {
|
for errScanner.Scan() {
|
||||||
m.Ui.Error(errScanner.Text())
|
c.Ui.Error(errScanner.Text())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
f.SetOutput(errW)
|
f.SetOutput(errW)
|
||||||
|
|
||||||
m.flagSet = f
|
c.flagSet = f
|
||||||
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse is used to parse the underlying flag set.
|
// Parse is used to parse the underlying flag set.
|
||||||
func (m *Meta) Parse(args []string) error {
|
func (c *Command) Parse(args []string) error {
|
||||||
return m.flagSet.Parse(args)
|
return c.flagSet.Parse(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Help returns the help for this flagSet.
|
// Help returns the help for this flagSet.
|
||||||
func (m *Meta) Help() string {
|
func (c *Command) Help() string {
|
||||||
return m.helpFlagsFor(m.flagSet)
|
return c.helpFlagsFor(c.flagSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasHTTP returns true if this meta command contains HTTP flags.
|
// hasClientHTTP returns true if this meta command contains client HTTP flags.
|
||||||
func (m *Meta) hasHTTP() bool {
|
func (c *Command) hasClientHTTP() bool {
|
||||||
return m.Flags&FlagSetHTTP != 0
|
return c.Flags&FlagSetClientHTTP != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasServerHTTP returns true if this meta command contains server HTTP flags.
|
||||||
|
func (c *Command) hasServerHTTP() bool {
|
||||||
|
return c.Flags&FlagSetServerHTTP != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// helpFlagsFor visits all flags in the given flag set and prints formatted
|
// helpFlagsFor visits all flags in the given flag set and prints formatted
|
||||||
// help output. This function is sad because there's no "merging" of command
|
// help output. This function is sad because there's no "merging" of command
|
||||||
// line flags. We explicitly pull out our "common" options into another section
|
// line flags. We explicitly pull out our "common" options into another section
|
||||||
// by doing string comparisons :(.
|
// by doing string comparisons :(.
|
||||||
func (m *Meta) helpFlagsFor(f *flag.FlagSet) string {
|
func (c *Command) helpFlagsFor(f *flag.FlagSet) string {
|
||||||
httpFlags := m.httpFlags(nil)
|
httpFlagsClient := c.httpFlagsClient(nil)
|
||||||
|
httpFlagsServer := c.httpFlagsServer(nil)
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
first := true
|
firstHTTP := true
|
||||||
f.VisitAll(func(f *flag.Flag) {
|
if c.hasClientHTTP() {
|
||||||
// Skip HTTP flags as they will be grouped separately
|
if firstHTTP {
|
||||||
if flagContains(httpFlags, f) {
|
printTitle(&out, "HTTP API Options")
|
||||||
return
|
firstHTTP = false
|
||||||
}
|
}
|
||||||
if first {
|
httpFlagsClient.VisitAll(func(f *flag.Flag) {
|
||||||
printTitle(&out, "Command Options")
|
|
||||||
first = false
|
|
||||||
}
|
|
||||||
printFlag(&out, f)
|
|
||||||
})
|
|
||||||
|
|
||||||
if m.hasHTTP() {
|
|
||||||
printTitle(&out, "HTTP API Options")
|
|
||||||
httpFlags.VisitAll(func(f *flag.Flag) {
|
|
||||||
printFlag(&out, f)
|
printFlag(&out, f)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if c.hasServerHTTP() {
|
||||||
|
if firstHTTP {
|
||||||
|
printTitle(&out, "HTTP API Options")
|
||||||
|
firstHTTP = false
|
||||||
|
}
|
||||||
|
httpFlagsServer.VisitAll(func(f *flag.Flag) {
|
||||||
|
printFlag(&out, f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
firstCommand := true
|
||||||
|
f.VisitAll(func(f *flag.Flag) {
|
||||||
|
// Skip HTTP flags as they will be grouped separately
|
||||||
|
if flagContains(httpFlagsClient, f) || flagContains(httpFlagsServer, f) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if firstCommand {
|
||||||
|
printTitle(&out, "Command Options")
|
||||||
|
firstCommand = false
|
||||||
|
}
|
||||||
|
printFlag(&out, f)
|
||||||
|
})
|
||||||
|
|
||||||
return strings.TrimRight(out.String(), "\n")
|
return strings.TrimRight(out.String(), "\n")
|
||||||
}
|
}
|
||||||
|
@ -190,7 +233,7 @@ func flagContains(fs *flag.FlagSet, f *flag.Flag) bool {
|
||||||
return skip
|
return skip
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrapAtLength wraps the given text at the maxLineLength, taxing into account
|
// wrapAtLength wraps the given text at the maxLineLength, taking into account
|
||||||
// any provided left padding.
|
// any provided left padding.
|
||||||
func wrapAtLength(s string, pad int) string {
|
func wrapAtLength(s string, pad int) string {
|
||||||
wrapped := text.Wrap(s, maxLineLength-pad)
|
wrapped := text.Wrap(s, maxLineLength-pad)
|
|
@ -5,14 +5,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/command/agent"
|
"github.com/hashicorp/consul/command/agent"
|
||||||
|
"github.com/hashicorp/consul/command/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigTestCommand is a Command implementation that is used to
|
// ConfigTestCommand is a Command implementation that is used to
|
||||||
// verify config files
|
// verify config files
|
||||||
type ConfigTestCommand struct {
|
type ConfigTestCommand struct {
|
||||||
Meta
|
base.Command
|
||||||
|
|
||||||
configFiles []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigTestCommand) Help() string {
|
func (c *ConfigTestCommand) Help() string {
|
||||||
|
@ -27,29 +26,31 @@ Usage: consul configtest [options]
|
||||||
|
|
||||||
Returns 0 if the configuration is valid, or 1 if there are problems.
|
Returns 0 if the configuration is valid, or 1 if there are problems.
|
||||||
|
|
||||||
` + c.Meta.Help()
|
` + c.Command.Help()
|
||||||
|
|
||||||
return strings.TrimSpace(helpText)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigTestCommand) Run(args []string) int {
|
func (c *ConfigTestCommand) Run(args []string) int {
|
||||||
f := c.Meta.NewFlagSet(c)
|
var configFiles []string
|
||||||
f.Var((*agent.AppendSliceValue)(&c.configFiles), "config-file",
|
|
||||||
|
f := c.Command.NewFlagSet(c)
|
||||||
|
f.Var((*agent.AppendSliceValue)(&configFiles), "config-file",
|
||||||
"Path to a JSON file to read configuration from. This can be specified multiple times.")
|
"Path to a JSON file to read configuration from. This can be specified multiple times.")
|
||||||
f.Var((*agent.AppendSliceValue)(&c.configFiles), "config-dir",
|
f.Var((*agent.AppendSliceValue)(&configFiles), "config-dir",
|
||||||
"Path to a directory to read configuration files from. This will read every file ending in "+
|
"Path to a directory to read configuration files from. This will read every file ending in "+
|
||||||
".json as configuration in this directory in alphabetical order.")
|
".json as configuration in this directory in alphabetical order.")
|
||||||
|
|
||||||
if err := c.Meta.Parse(args); err != nil {
|
if err := c.Command.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.configFiles) <= 0 {
|
if len(configFiles) <= 0 {
|
||||||
c.Ui.Error("Must specify config using -config-file or -config-dir")
|
c.Ui.Error("Must specify config using -config-file or -config-dir")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := agent.ReadConfigPaths(c.configFiles)
|
_, err := agent.ReadConfigPaths(configFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Config validation failed: %v", err.Error()))
|
c.Ui.Error(fmt.Sprintf("Config validation failed: %v", err.Error()))
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -6,15 +6,16 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/command/base"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testConfigTestCommand(t *testing.T) (*cli.MockUi, *ConfigTestCommand) {
|
func testConfigTestCommand(t *testing.T) (*cli.MockUi, *ConfigTestCommand) {
|
||||||
ui := new(cli.MockUi)
|
ui := new(cli.MockUi)
|
||||||
return ui, &ConfigTestCommand{
|
return ui, &ConfigTestCommand{
|
||||||
Meta: Meta{
|
Command: base.Command{
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
Flags: FlagSetNone,
|
Flags: base.FlagSetNone,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,19 @@ package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/hashicorp/consul/command/base"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ForceLeaveCommand is a Command implementation that tells a running Consul
|
// ForceLeaveCommand is a Command implementation that tells a running Consul
|
||||||
// to force a member to enter the "left" state.
|
// to force a member to enter the "left" state.
|
||||||
type ForceLeaveCommand struct {
|
type ForceLeaveCommand struct {
|
||||||
Meta
|
base.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ForceLeaveCommand) Run(args []string) int {
|
func (c *ForceLeaveCommand) Run(args []string) int {
|
||||||
f := c.Meta.NewFlagSet(c)
|
f := c.Command.NewFlagSet(c)
|
||||||
if err := c.Meta.Parse(args); err != nil {
|
if err := c.Command.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ func (c *ForceLeaveCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := c.Meta.HTTPClient()
|
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
|
||||||
|
@ -55,7 +56,7 @@ Usage: consul force-leave [options] name
|
||||||
Consul will attempt to reconnect to those failed nodes for some period of
|
Consul will attempt to reconnect to those failed nodes for some period of
|
||||||
time before eventually reaping them.
|
time before eventually reaping them.
|
||||||
|
|
||||||
` + c.Meta.Help()
|
` + c.Command.Help()
|
||||||
|
|
||||||
return strings.TrimSpace(helpText)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package command
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/hashicorp/consul/command/base"
|
||||||
"github.com/hashicorp/consul/testutil"
|
"github.com/hashicorp/consul/testutil"
|
||||||
"github.com/hashicorp/serf/serf"
|
"github.com/hashicorp/serf/serf"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
@ -13,9 +14,9 @@ import (
|
||||||
func testForceLeaveCommand(t *testing.T) (*cli.MockUi, *ForceLeaveCommand) {
|
func testForceLeaveCommand(t *testing.T) (*cli.MockUi, *ForceLeaveCommand) {
|
||||||
ui := new(cli.MockUi)
|
ui := new(cli.MockUi)
|
||||||
return ui, &ForceLeaveCommand{
|
return ui, &ForceLeaveCommand{
|
||||||
Meta: Meta{
|
Command: base.Command{
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
Flags: FlagSetHTTP,
|
Flags: base.FlagSetClientHTTP,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/consul/command/agent"
|
"github.com/hashicorp/consul/command/agent"
|
||||||
|
"github.com/hashicorp/consul/command/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -35,19 +36,14 @@ const (
|
||||||
// LockCommand is a Command implementation that is used to setup
|
// LockCommand is a Command implementation that is used to setup
|
||||||
// a "lock" which manages lock acquisition and invokes a sub-process
|
// a "lock" which manages lock acquisition and invokes a sub-process
|
||||||
type LockCommand struct {
|
type LockCommand struct {
|
||||||
Meta
|
base.Command
|
||||||
|
|
||||||
ShutdownCh <-chan struct{}
|
ShutdownCh <-chan struct{}
|
||||||
|
|
||||||
child *os.Process
|
child *os.Process
|
||||||
childLock sync.Mutex
|
childLock sync.Mutex
|
||||||
|
|
||||||
limit int
|
verbose bool
|
||||||
monitorRetry int
|
|
||||||
name string
|
|
||||||
passStdin bool
|
|
||||||
timeout time.Duration
|
|
||||||
verbose bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *LockCommand) Help() string {
|
func (c *LockCommand) Help() string {
|
||||||
|
@ -69,7 +65,7 @@ Usage: consul lock [options] prefix child...
|
||||||
|
|
||||||
The prefix provided must have write privileges.
|
The prefix provided must have write privileges.
|
||||||
|
|
||||||
` + c.Meta.Help()
|
` + c.Command.Help()
|
||||||
|
|
||||||
return strings.TrimSpace(helpText)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
@ -81,39 +77,44 @@ func (c *LockCommand) Run(args []string) int {
|
||||||
|
|
||||||
func (c *LockCommand) run(args []string, lu **LockUnlock) int {
|
func (c *LockCommand) run(args []string, lu **LockUnlock) int {
|
||||||
var childDone chan struct{}
|
var childDone chan struct{}
|
||||||
|
var limit int
|
||||||
|
var monitorRetry int
|
||||||
|
var name string
|
||||||
|
var passStdin bool
|
||||||
|
var timeout time.Duration
|
||||||
|
|
||||||
f := c.Meta.NewFlagSet(c)
|
f := c.Command.NewFlagSet(c)
|
||||||
f.IntVar(&c.limit, "n", 1,
|
f.IntVar(&limit, "n", 1,
|
||||||
"Optional limit on the number of concurrent lock holders. The underlying "+
|
"Optional limit on the number of concurrent lock holders. The underlying "+
|
||||||
"implementation switches from a lock to a semaphore when the value is "+
|
"implementation switches from a lock to a semaphore when the value is "+
|
||||||
"greater than 1. The default value is 1.")
|
"greater than 1. The default value is 1.")
|
||||||
f.IntVar(&c.monitorRetry, "monitor-retry", defaultMonitorRetry,
|
f.IntVar(&monitorRetry, "monitor-retry", defaultMonitorRetry,
|
||||||
"Number of times to retry Consul returns a 500 error while monitoring "+
|
"Number of times to retry if Consul returns a 500 error while monitoring "+
|
||||||
"the lock. This allows riding out brief periods of unavailability "+
|
"the lock. This allows riding out brief periods of unavailability "+
|
||||||
"without causing leader elections, but increases the amount of time "+
|
"without causing leader elections, but increases the amount of time "+
|
||||||
"required to detect a lost lock in some cases. The default value is 3, "+
|
"required to detect a lost lock in some cases. The default value is 3, "+
|
||||||
"with a 1s wait between retries. Set this value to 0 to disable retires.")
|
"with a 1s wait between retries. Set this value to 0 to disable retires.")
|
||||||
f.StringVar(&c.name, "name", "",
|
f.StringVar(&name, "name", "",
|
||||||
"Optional name to associate with the lock session. It not provided, one "+
|
"Optional name to associate with the lock session. It not provided, one "+
|
||||||
"is generated based on the provided child command.")
|
"is generated based on the provided child command.")
|
||||||
f.BoolVar(&c.passStdin, "pass-stdin", false,
|
f.BoolVar(&passStdin, "pass-stdin", false,
|
||||||
"Pass stdin to the child process.")
|
"Pass stdin to the child process.")
|
||||||
f.DurationVar(&c.timeout, "timeout", 0,
|
f.DurationVar(&timeout, "timeout", 0,
|
||||||
"Maximum amount of time to wait to acquire the lock, specified as a "+
|
"Maximum amount of time to wait to acquire the lock, specified as a "+
|
||||||
"timestamp like \"1s\" or \"3h\". The default value is 0.")
|
"timestamp like \"1s\" or \"3h\". The default value is 0.")
|
||||||
f.BoolVar(&c.verbose, "verbose", false,
|
f.BoolVar(&c.verbose, "verbose", false,
|
||||||
"Enable verbose (debugging) output.")
|
"Enable verbose (debugging) output.")
|
||||||
|
|
||||||
// Deprecations
|
// Deprecations
|
||||||
f.DurationVar(&c.timeout, "try", 0,
|
f.DurationVar(&timeout, "try", 0,
|
||||||
"DEPRECATED. Use -timeout instead.")
|
"DEPRECATED. Use -timeout instead.")
|
||||||
|
|
||||||
if err := c.Meta.Parse(args); err != nil {
|
if err := c.Command.Parse(args); err != nil {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the limit
|
// Check the limit
|
||||||
if c.limit <= 0 {
|
if limit <= 0 {
|
||||||
c.Ui.Error(fmt.Sprintf("Lock holder limit must be positive"))
|
c.Ui.Error(fmt.Sprintf("Lock holder limit must be positive"))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -128,27 +129,27 @@ func (c *LockCommand) run(args []string, lu **LockUnlock) int {
|
||||||
prefix = strings.TrimPrefix(prefix, "/")
|
prefix = strings.TrimPrefix(prefix, "/")
|
||||||
script := strings.Join(extra[1:], " ")
|
script := strings.Join(extra[1:], " ")
|
||||||
|
|
||||||
if c.timeout < 0 {
|
if timeout < 0 {
|
||||||
c.Ui.Error("Timeout must be positive")
|
c.Ui.Error("Timeout must be positive")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate a session name if none provided
|
// Calculate a session name if none provided
|
||||||
if c.name == "" {
|
if name == "" {
|
||||||
c.name = fmt.Sprintf("Consul lock for '%s' at '%s'", script, prefix)
|
name = fmt.Sprintf("Consul lock for '%s' at '%s'", script, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate oneshot
|
// Calculate oneshot
|
||||||
oneshot := c.timeout > 0
|
oneshot := timeout > 0
|
||||||
|
|
||||||
// Check the retry parameter
|
// Check the retry parameter
|
||||||
if c.monitorRetry < 0 {
|
if monitorRetry < 0 {
|
||||||
c.Ui.Error("Number for 'monitor-retry' must be >= 0")
|
c.Ui.Error("Number for 'monitor-retry' must be >= 0")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and test the HTTP client
|
// Create and test the HTTP client
|
||||||
client, err := c.Meta.HTTPClient()
|
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
|
||||||
|
@ -160,10 +161,10 @@ func (c *LockCommand) run(args []string, lu **LockUnlock) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the lock or semaphore
|
// Setup the lock or semaphore
|
||||||
if c.limit == 1 {
|
if limit == 1 {
|
||||||
*lu, err = c.setupLock(client, prefix, c.name, oneshot, c.timeout, c.monitorRetry)
|
*lu, err = c.setupLock(client, prefix, name, oneshot, timeout, monitorRetry)
|
||||||
} else {
|
} else {
|
||||||
*lu, err = c.setupSemaphore(client, c.limit, prefix, c.name, oneshot, c.timeout, c.monitorRetry)
|
*lu, err = c.setupSemaphore(client, limit, prefix, name, oneshot, timeout, monitorRetry)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Lock setup failed: %s", err))
|
c.Ui.Error(fmt.Sprintf("Lock setup failed: %s", err))
|
||||||
|
@ -195,7 +196,7 @@ func (c *LockCommand) run(args []string, lu **LockUnlock) int {
|
||||||
// Start the child process
|
// Start the child process
|
||||||
childDone = make(chan struct{})
|
childDone = make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
if err := c.startChild(script, childDone, c.passStdin); err != nil {
|
if err := c.startChild(script, childDone, passStdin); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("%s", err))
|
c.Ui.Error(fmt.Sprintf("%s", err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -9,15 +9,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/command/base"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testLockCommand(t *testing.T) (*cli.MockUi, *LockCommand) {
|
func testLockCommand(t *testing.T) (*cli.MockUi, *LockCommand) {
|
||||||
ui := new(cli.MockUi)
|
ui := new(cli.MockUi)
|
||||||
return ui, &LockCommand{
|
return ui, &LockCommand{
|
||||||
Meta: Meta{
|
Command: base.Command{
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
Flags: FlagSetHTTP,
|
Flags: base.FlagSetHTTP,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
commands.go
13
commands.go
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/command"
|
"github.com/hashicorp/consul/command"
|
||||||
"github.com/hashicorp/consul/command/agent"
|
"github.com/hashicorp/consul/command/agent"
|
||||||
|
"github.com/hashicorp/consul/command/base"
|
||||||
"github.com/hashicorp/consul/version"
|
"github.com/hashicorp/consul/version"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
@ -31,8 +32,8 @@ func init() {
|
||||||
|
|
||||||
"configtest": func() (cli.Command, error) {
|
"configtest": func() (cli.Command, error) {
|
||||||
return &command.ConfigTestCommand{
|
return &command.ConfigTestCommand{
|
||||||
Meta: command.Meta{
|
Command: base.Command{
|
||||||
Flags: command.FlagSetNone,
|
Flags: base.FlagSetNone,
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -53,8 +54,8 @@ func init() {
|
||||||
|
|
||||||
"force-leave": func() (cli.Command, error) {
|
"force-leave": func() (cli.Command, error) {
|
||||||
return &command.ForceLeaveCommand{
|
return &command.ForceLeaveCommand{
|
||||||
Meta: command.Meta{
|
Command: base.Command{
|
||||||
Flags: command.FlagSetHTTP,
|
Flags: base.FlagSetClientHTTP,
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -123,8 +124,8 @@ func init() {
|
||||||
"lock": func() (cli.Command, error) {
|
"lock": func() (cli.Command, error) {
|
||||||
return &command.LockCommand{
|
return &command.LockCommand{
|
||||||
ShutdownCh: makeShutdownCh(),
|
ShutdownCh: makeShutdownCh(),
|
||||||
Meta: command.Meta{
|
Command: base.Command{
|
||||||
Flags: command.FlagSetHTTP,
|
Flags: base.FlagSetHTTP,
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
* `-http-addr=<addr>` - Address of the Consul agent with the port. This can be
|
|
||||||
an IP address or DNS address, but it must include the port. This can also be
|
|
||||||
specified via the CONSUL_HTTP_ADDR environment variable. The default value is
|
|
||||||
127.0.0.1:8500.
|
|
||||||
|
|
||||||
* `-datacenter=<name>` - Name of the datacenter to query. If unspecified, the
|
|
||||||
query will default to the datacenter of the Consul agent at the HTTP address.
|
|
||||||
|
|
||||||
* `-token=<value>` - ACL token to use in the request. This can also be specified
|
|
||||||
via the `CONSUL_HTTP_TOKEN` environment variable. If unspecified, the query
|
|
||||||
will default to the token of the Consul agent at the HTTP address.
|
|
||||||
|
|
||||||
* `-stale` - Permit any Consul server (non-leader) to respond to this request.
|
|
||||||
This allows for lower latency and higher throughput, but can result in stale
|
|
||||||
data. This option has no effect on non-read operations. The default value is
|
|
||||||
false.
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
* `-http-addr=<addr>` - Address of the Consul agent with the port. This can be
|
||||||
|
an IP address or DNS address, but it must include the port. This can also be
|
||||||
|
specified via the `CONSUL_HTTP_ADDR` environment variable. The default value is
|
||||||
|
127.0.0.1:8500.
|
||||||
|
|
||||||
|
* `-token=<value>` - ACL token to use in the request. This can also be specified
|
||||||
|
via the `CONSUL_HTTP_TOKEN` environment variable. If unspecified, the query
|
||||||
|
will default to the token of the Consul agent at the HTTP address.
|
|
@ -0,0 +1,7 @@
|
||||||
|
* `-datacenter=<name>` - Name of the datacenter to query. If unspecified, the
|
||||||
|
query will default to the datacenter of the Consul agent at the HTTP address.
|
||||||
|
|
||||||
|
* `-stale` - Permit any Consul server (non-leader) to respond to this request.
|
||||||
|
This allows for lower latency and higher throughput, but can result in stale
|
||||||
|
data. This option has no effect on non-read operations. The default value is
|
||||||
|
false.
|
|
@ -28,11 +28,7 @@ as it will be removed from the Raft quorum.
|
||||||
|
|
||||||
Usage: `consul force-leave [options] node`
|
Usage: `consul force-leave [options] node`
|
||||||
|
|
||||||
The following command-line options are available for this command.
|
#### API Options
|
||||||
Every option is optional:
|
|
||||||
|
|
||||||
* `-rpc-addr` - Address to the RPC 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 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`.
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ Usage: `consul kv delete [options] KEY_OR_PREFIX`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
<%= partial "docs/commands/http_api_options_server" %>
|
||||||
|
|
||||||
#### KV Delete Options
|
#### KV Delete Options
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@ Usage: `consul kv export [PREFIX]`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
<%= partial "docs/commands/http_api_options_server" %>
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,8 @@ Usage: `consul kv get [options] [KEY_OR_PREFIX]`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
<%= partial "docs/commands/http_api_options_server" %>
|
||||||
|
|
||||||
#### KV Get Options
|
#### KV Get Options
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ Usage: `consul kv import [DATA]`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
<%= partial "docs/commands/http_api_options_server" %>
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ Usage: `consul kv put [options] KEY [DATA]`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
<%= partial "docs/commands/http_api_options_server" %>
|
||||||
|
|
||||||
#### KV Put Options
|
#### KV Put Options
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,18 @@ of 5 seconds, a `SIGKILL` will be used to force termination. For Consul agents
|
||||||
on Windows, the child process is always terminated with a `SIGKILL`, since
|
on Windows, the child process is always terminated with a `SIGKILL`, since
|
||||||
Windows has no POSIX compatible notion for `SIGTERM`.
|
Windows has no POSIX compatible notion for `SIGTERM`.
|
||||||
|
|
||||||
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.
|
|
||||||
|
#### Command Options
|
||||||
|
|
||||||
|
* `-monitor-retry` - Retry up to this number of times if Consul returns a 500 error
|
||||||
|
while monitoring the lock. This allows riding out brief periods of unavailability
|
||||||
|
without causing leader elections, but increases the amount of time required
|
||||||
|
to detect a lost lock in some cases. Defaults to 3, with a 1s wait between retries.
|
||||||
|
Set to 0 to disable.
|
||||||
|
|
||||||
* `-n` - Optional, limit of lock holders. Defaults to 1. The underlying
|
* `-n` - Optional, limit of lock holders. Defaults to 1. The underlying
|
||||||
implementation switches from a lock to a semaphore when increased past
|
implementation switches from a lock to a semaphore when increased past
|
||||||
|
@ -58,20 +65,12 @@ The list of available flags are:
|
||||||
* `-name` - Optional name to associate with the underlying session.
|
* `-name` - Optional name to associate with the underlying session.
|
||||||
If not provided, one is generated based on the child command.
|
If not provided, one is generated based on the child command.
|
||||||
|
|
||||||
* `-token` - ACL token to use. Defaults to that of agent.
|
|
||||||
|
|
||||||
* `-pass-stdin` - Pass stdin to child process.
|
* `-pass-stdin` - Pass stdin to child process.
|
||||||
|
|
||||||
* `-try` - Attempt to acquire the lock up to the given timeout. The timeout is a
|
* `-try` - Attempt to acquire the lock up to the given timeout. The timeout is a
|
||||||
positive decimal number, with unit suffix, such as "500ms". Valid time units
|
positive decimal number, with unit suffix, such as "500ms". Valid time units
|
||||||
are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||||
|
|
||||||
* `-monitor-retry` - Retry up to this number of times if Consul returns a 500 error
|
|
||||||
while monitoring the lock. This allows riding out brief periods of unavailability
|
|
||||||
without causing leader elections, but increases the amount of time required
|
|
||||||
to detect a lost lock in some cases. Defaults to 3, with a 1s wait between retries.
|
|
||||||
Set to 0 to disable.
|
|
||||||
|
|
||||||
* `-verbose` - Enables verbose output.
|
* `-verbose` - Enables verbose output.
|
||||||
|
|
||||||
## SHELL
|
## SHELL
|
|
@ -60,7 +60,7 @@ Usage: `consul snapshot agent [options]`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
|
||||||
#### Config File Options:
|
#### Config File Options:
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ Usage: `consul snapshot restore [options] FILE`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ Usage: `consul snapshot save [options] FILE`
|
||||||
|
|
||||||
#### API Options
|
#### API Options
|
||||||
|
|
||||||
<%= partial "docs/commands/http_api_options" %>
|
<%= partial "docs/commands/http_api_options_client" %>
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue