commands: make sure the correct flagset is used

A lot of commands used `c.Meta.flagSet()` to create the initial flagset for the command, while quite a few of them didn’t actually use or support the flags that are then added.

So I updated a few commands to use `flag.NewFlagSet()` instead to only add the flags that are actually needed/supported.

Additionally this prevents a few commands from using locking while they actually don’t need locking (as locking is enabled as a default in `c.Meta.flagSet()`.
This commit is contained in:
Sander van Harmelen 2018-11-21 15:35:27 +01:00
parent 7d5db9522f
commit ef9054562e
34 changed files with 227 additions and 196 deletions

View File

@ -29,7 +29,7 @@ func (c *ZeroTwelveUpgradeCommand) Run(args []string) int {
var skipConfirm, force bool
flags := c.Meta.flagSet("0.12upgrade")
flags := c.Meta.extendedFlagSet("0.12upgrade")
flags.BoolVar(&skipConfirm, "yes", false, "skip confirmation prompt")
flags.BoolVar(&force, "force", false, "override duplicate upgrade heuristic")
if err := flags.Parse(args); err != nil {

View File

@ -38,7 +38,7 @@ func (c *ApplyCommand) Run(args []string) int {
cmdName = "destroy"
}
cmdFlags := c.Meta.flagSet(cmdName)
cmdFlags := c.Meta.extendedFlagSet(cmdName)
cmdFlags.BoolVar(&autoApprove, "auto-approve", false, "skip interactive approval of plan before applying")
if c.Destroy {
cmdFlags.BoolVar(&destroyForce, "force", false, "deprecated: same as auto-approve")

View File

@ -25,7 +25,7 @@ func (c *ConsoleCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("console")
cmdFlags := c.Meta.defaultFlagSet("console")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
@ -74,6 +74,7 @@ func (c *ConsoleCommand) Run(args []string) int {
c.showDiagnostics(diags)
return 1
}
{
var moreDiags tfdiags.Diagnostics
opReq.Variables, moreDiags = c.collectVariableValues()

View File

@ -20,7 +20,7 @@ func (c *DebugJSON2DotCommand) Run(args []string) int {
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("debug json2dot")
cmdFlags := c.Meta.extendedFlagSet("debug json2dot")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp

View File

@ -2,7 +2,6 @@ package command
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
@ -46,14 +45,13 @@ func (c *FmtCommand) Run(args []string) int {
return 1
}
cmdFlags := flag.NewFlagSet("fmt", flag.ContinueOnError)
cmdFlags := c.Meta.defaultFlagSet("fmt")
cmdFlags.BoolVar(&c.list, "list", true, "list")
cmdFlags.BoolVar(&c.write, "write", true, "write")
cmdFlags.BoolVar(&c.diff, "diff", false, "diff")
cmdFlags.BoolVar(&c.check, "check", false, "check")
cmdFlags.BoolVar(&c.recursive, "recursive", false, "recursive")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}

View File

@ -1,7 +1,6 @@
package command
import (
"flag"
"fmt"
"strings"
@ -22,7 +21,7 @@ func (c *GetCommand) Run(args []string) int {
return 1
}
cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError)
cmdFlags := c.Meta.defaultFlagSet("get")
cmdFlags.BoolVar(&update, "update", false, "update")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {

View File

@ -1,7 +1,6 @@
package command
import (
"flag"
"fmt"
"strings"
@ -20,21 +19,21 @@ type GraphCommand struct {
}
func (c *GraphCommand) Run(args []string) int {
var moduleDepth int
var verbose bool
var drawCycles bool
var graphTypeStr string
var moduleDepth int
var verbose bool
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
cmdFlags := flag.NewFlagSet("graph", flag.ContinueOnError)
cmdFlags.IntVar(&moduleDepth, "module-depth", -1, "module-depth")
cmdFlags.BoolVar(&verbose, "verbose", false, "verbose")
cmdFlags := c.Meta.defaultFlagSet("graph")
cmdFlags.BoolVar(&drawCycles, "draw-cycles", false, "draw-cycles")
cmdFlags.StringVar(&graphTypeStr, "type", "", "type")
cmdFlags.IntVar(&moduleDepth, "module-depth", -1, "module-depth")
cmdFlags.BoolVar(&verbose, "verbose", false, "verbose")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -187,12 +186,9 @@ Options:
-module-depth=n Specifies the depth of modules to show in the output.
By default this is -1, which will expand all.
-no-color If specified, output won't contain any color.
-type=plan Type of graph to output. Can be: plan, plan-destroy, apply,
validate, input, refresh.
`
return strings.TrimSpace(helpText)
}

View File

@ -37,7 +37,7 @@ func (c *ImportCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("import")
cmdFlags := c.Meta.extendedFlagSet("import")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")

View File

@ -50,7 +50,8 @@ func (c *InitCommand) Run(args []string) int {
if err != nil {
return 1
}
cmdFlags := c.flagSet("init")
cmdFlags := c.Meta.extendedFlagSet("init")
cmdFlags.BoolVar(&flagBackend, "backend", true, "")
cmdFlags.Var(flagConfigExtra, "backend-config", "")
cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init")
@ -63,7 +64,6 @@ func (c *InitCommand) Run(args []string) int {
cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "")
cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory")
cmdFlags.BoolVar(&flagVerifyPlugins, "verify-plugins", true, "verify plugins")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1

View File

@ -136,8 +136,6 @@ type Meta struct {
// parallelism is used to control the number of concurrent operations
// allowed when walking the graph
//
// shadow is used to enable/disable the shadow graph
//
// provider is to specify specific resource providers
//
// stateLock is set to false to disable state locking
@ -153,7 +151,6 @@ type Meta struct {
stateOutPath string
backupPath string
parallelism int
shadow bool
provider string
stateLock bool
stateLockTimeout time.Duration
@ -350,25 +347,9 @@ func (m *Meta) contextOpts() *terraform.ContextOpts {
return &opts
}
// flags adds the meta flags to the given FlagSet.
func (m *Meta) flagSet(n string) *flag.FlagSet {
// defaultFlagSet creates a default flag set for commands.
func (m *Meta) defaultFlagSet(n string) *flag.FlagSet {
f := flag.NewFlagSet(n, flag.ContinueOnError)
f.BoolVar(&m.input, "input", true, "input")
f.Var((*FlagTargetSlice)(&m.targets), "target", "resource to target")
if m.variableArgs.items == nil {
m.variableArgs = newRawFlags("-var")
}
varValues := m.variableArgs.Alias("-var")
varFiles := m.variableArgs.Alias("-var-file")
f.Var(varValues, "var", "variables")
f.Var(varFiles, "var-file", "variable file")
// Advanced (don't need documentation, or unlikely to be set)
f.BoolVar(&m.shadow, "shadow", true, "shadow graph")
// Experimental features
experiment.Flag(f)
// Create an io.Writer that writes to our Ui properly for errors.
// This is kind of a hack, but it does the job. Basically: create
@ -393,8 +374,30 @@ func (m *Meta) flagSet(n string) *flag.FlagSet {
// Set the default Usage to empty
f.Usage = func() {}
// command that bypass locking will supply their own flag on this var, but
// set the initial meta value to true as a failsafe.
return f
}
// extendedFlagSet adds custom flags that are mostly used by commands
// that are used to run an operation like plan or apply.
func (m *Meta) extendedFlagSet(n string) *flag.FlagSet {
f := m.defaultFlagSet(n)
f.BoolVar(&m.input, "input", true, "input")
f.Var((*FlagTargetSlice)(&m.targets), "target", "resource to target")
if m.variableArgs.items == nil {
m.variableArgs = newRawFlags("-var")
}
varValues := m.variableArgs.Alias("-var")
varFiles := m.variableArgs.Alias("-var-file")
f.Var(varValues, "var", "variables")
f.Var(varFiles, "var-file", "variable file")
// Experimental features
experiment.Flag(f)
// commands that bypass locking will supply their own flag on this var,
// but set the initial meta value to true as a failsafe.
m.stateLock = true
return f

View File

@ -1864,7 +1864,7 @@ func testMetaBackend(t *testing.T, args []string) *Meta {
var m Meta
m.Ui = new(cli.MockUi)
m.process(args, true)
f := m.flagSet("test")
f := m.extendedFlagSet("test")
if err := f.Parse(args); err != nil {
t.Fatalf("unexpected error: %s", err)
}

View File

@ -389,6 +389,9 @@ func (f rawFlags) Empty() bool {
}
func (f rawFlags) AllItems() []rawFlag {
if f.items == nil {
return nil
}
return *f.items
}

View File

@ -73,7 +73,7 @@ func TestMetaInputMode(t *testing.T) {
m := new(Meta)
args := []string{}
fs := m.flagSet("foo")
fs := m.extendedFlagSet("foo")
if err := fs.Parse(args); err != nil {
t.Fatalf("err: %s", err)
}
@ -92,7 +92,7 @@ func TestMetaInputMode_envVar(t *testing.T) {
m := new(Meta)
args := []string{}
fs := m.flagSet("foo")
fs := m.extendedFlagSet("foo")
if err := fs.Parse(args); err != nil {
t.Fatalf("err: %s", err)
}
@ -124,7 +124,7 @@ func TestMetaInputMode_disable(t *testing.T) {
m := new(Meta)
args := []string{"-input=false"}
fs := m.flagSet("foo")
fs := m.extendedFlagSet("foo")
if err := fs.Parse(args); err != nil {
t.Fatalf("err: %s", err)
}
@ -160,7 +160,7 @@ func TestMetaInputMode_defaultVars(t *testing.T) {
t.Fatalf("err: %s", err)
}
fs := m.flagSet("foo")
fs := m.extendedFlagSet("foo")
if err := fs.Parse(args); err != nil {
t.Fatalf("err: %s", err)
}
@ -177,7 +177,7 @@ func TestMetaInputMode_vars(t *testing.T) {
m := new(Meta)
args := []string{"-var", "foo=bar"}
fs := m.flagSet("foo")
fs := m.extendedFlagSet("foo")
if err := fs.Parse(args); err != nil {
t.Fatalf("err: %s", err)
}

View File

@ -3,7 +3,6 @@ package command
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"sort"
"strings"
@ -31,7 +30,7 @@ func (c *OutputCommand) Run(args []string) int {
var module string
var jsonOutput bool
cmdFlags := flag.NewFlagSet("output", flag.ContinueOnError)
cmdFlags := c.Meta.defaultFlagSet("output")
cmdFlags.BoolVar(&jsonOutput, "json", false, "json")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&module, "module", "", "module")

View File

@ -25,12 +25,11 @@ func (c *PlanCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("plan")
cmdFlags := c.Meta.extendedFlagSet("plan")
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
cmdFlags.StringVar(&outPath, "out", "", "path")
cmdFlags.IntVar(
&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")

View File

@ -29,7 +29,7 @@ func (c *ProvidersCommand) Synopsis() string {
func (c *ProvidersCommand) Run(args []string) int {
c.Meta.process(args, false)
cmdFlags := c.Meta.flagSet("providers")
cmdFlags := c.Meta.defaultFlagSet("providers")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1

View File

@ -21,7 +21,7 @@ func (c *RefreshCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("refresh")
cmdFlags := c.Meta.extendedFlagSet("refresh")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
@ -72,13 +72,15 @@ func (c *RefreshCommand) Run(args []string) int {
// Build the operation
opReq := c.Operation(b)
opReq.Type = backend.OperationTypeRefresh
opReq.ConfigDir = configPath
opReq.Type = backend.OperationTypeRefresh
opReq.ConfigLoader, err = c.initConfigLoader()
if err != nil {
c.showDiagnostics(err)
return 1
}
{
var moreDiags tfdiags.Diagnostics
opReq.Variables, moreDiags = c.collectVariableValues()

View File

@ -1,7 +1,6 @@
package command
import (
"flag"
"fmt"
"os"
"strings"
@ -28,8 +27,7 @@ func (c *ShowCommand) Run(args []string) int {
return 1
}
cmdFlags := flag.NewFlagSet("show", flag.ContinueOnError)
cmdFlags := c.Meta.defaultFlagSet("show")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -87,6 +85,7 @@ func (c *ShowCommand) Run(args []string) int {
return 1
}
// Get the schemas from the context
schemas := ctx.Schemas()
env := c.Workspace()

View File

@ -21,7 +21,7 @@ func (c *StateListCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("state list")
cmdFlags := c.Meta.defaultFlagSet("state list")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
lookupId := cmdFlags.String("id", "", "Restrict output to paths with a resource having the specified ID.")
if err := cmdFlags.Parse(args); err != nil {

View File

@ -24,11 +24,13 @@ func (c *StateMvCommand) Run(args []string) int {
var backupPathOut, statePathOut string
var dryRun bool
cmdFlags := c.Meta.flagSet("state mv")
cmdFlags := c.Meta.defaultFlagSet("state mv")
cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run")
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
cmdFlags.StringVar(&c.statePath, "state", "", "path")
cmdFlags.StringVar(&backupPathOut, "backup-out", "-", "backup")
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock states")
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.StringVar(&c.statePath, "state", "", "path")
cmdFlags.StringVar(&statePathOut, "state-out", "", "path")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp
@ -301,6 +303,10 @@ Options:
to be specified if -state-out is set to a different path
than -state.
-lock=true Lock the state files when locking is supported.
-lock-timeout=0s Duration to retry a state lock.
-state=PATH Path to the source state file. Defaults to the configured
backend, or "terraform.tfstate"

View File

@ -22,7 +22,7 @@ func (c *StatePullCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("state pull")
cmdFlags := c.Meta.defaultFlagSet("state pull")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp
}

View File

@ -26,8 +26,10 @@ func (c *StatePushCommand) Run(args []string) int {
}
var flagForce bool
cmdFlags := c.Meta.flagSet("state push")
cmdFlags := c.Meta.defaultFlagSet("state push")
cmdFlags.BoolVar(&flagForce, "force", false, "")
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp
}
@ -139,6 +141,10 @@ Options:
-force Write the state even if lineages don't match or the
remote serial is higher.
-lock=true Lock the state file when locking is supported.
-lock-timeout=0s Duration to retry a state lock.
`
return strings.TrimSpace(helpText)
}

View File

@ -22,15 +22,17 @@ func (c *StateRmCommand) Run(args []string) int {
}
var dryRun bool
cmdFlags := c.Meta.flagSet("state show")
cmdFlags := c.Meta.defaultFlagSet("state rm")
cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run")
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.StringVar(&c.statePath, "state", "", "path")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp
}
args = cmdFlags.Args()
args = cmdFlags.Args()
if len(args) < 1 {
c.Ui.Error("At least one address is required.\n")
return cli.RunResultHelp
@ -165,6 +167,10 @@ Options:
will write it to the same path as the statefile with
a backup extension.
-lock=true Lock the state file when locking is supported.
-lock-timeout=0s Duration to retry a state lock.
-state=PATH Path to the source state file. Defaults to the configured
backend, or "terraform.tfstate"

View File

@ -24,7 +24,7 @@ func (c *StateShowCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("state show")
cmdFlags := c.Meta.defaultFlagSet("state show")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp
@ -66,6 +66,7 @@ func (c *StateShowCommand) Run(args []string) int {
// Build the operation (required to get the schemas)
opReq := c.Operation(b)
opReq.ConfigDir = cwd
opReq.ConfigLoader, err = c.initConfigLoader()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing config loader: %s", err))
@ -79,14 +80,6 @@ func (c *StateShowCommand) Run(args []string) int {
return 1
}
// Make sure to unlock the state
defer func() {
err := opReq.StateLocker.Unlock(nil)
if err != nil {
c.Ui.Error(err.Error())
}
}()
// Get the schemas from the context
schemas := ctx.Schemas()

View File

@ -5,10 +5,9 @@ import (
"fmt"
"strings"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
)
@ -24,16 +23,16 @@ func (c *TaintCommand) Run(args []string) int {
return 1
}
var allowMissing bool
var module string
cmdFlags := c.Meta.flagSet("taint")
var allowMissing bool
cmdFlags := c.Meta.defaultFlagSet("taint")
cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "module")
cmdFlags.StringVar(&module, "module", "", "module")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.StringVar(&module, "module", "", "module")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -199,8 +198,6 @@ Options:
-lock-timeout=0s Duration to retry a state lock.
-no-color If specified, output won't contain any color.
-state=path Path to read and save state (unless state-out
is specified). Defaults to "terraform.tfstate".

View File

@ -23,8 +23,8 @@ func (c *UnlockCommand) Run(args []string) int {
return 1
}
force := false
cmdFlags := c.Meta.flagSet("force-unlock")
var force bool
cmdFlags := c.Meta.defaultFlagSet("force-unlock")
cmdFlags.BoolVar(&force, "force", false, "force")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
@ -68,13 +68,13 @@ func (c *UnlockCommand) Run(args []string) int {
}
env := c.Workspace()
st, err := b.StateMgr(env)
stateMgr, err := b.StateMgr(env)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
return 1
}
_, isLocal := st.(*statemgr.Filesystem)
_, isLocal := stateMgr.(*statemgr.Filesystem)
if !force {
// Forcing this doesn't do anything, but doesn't break anything either,
@ -103,7 +103,7 @@ func (c *UnlockCommand) Run(args []string) int {
}
}
if err := st.Unlock(lockID); err != nil {
if err := stateMgr.Unlock(lockID); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to unlock state: %s", err))
return 1
}

View File

@ -5,12 +5,10 @@ import (
"fmt"
"strings"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
)
// UntaintCommand is a cli.Command implementation that manually untaints
@ -25,16 +23,16 @@ func (c *UntaintCommand) Run(args []string) int {
return 1
}
var allowMissing bool
var module string
cmdFlags := c.Meta.flagSet("untaint")
var allowMissing bool
cmdFlags := c.Meta.defaultFlagSet("untaint")
cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "module")
cmdFlags.StringVar(&module, "module", "", "module")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.StringVar(&module, "module", "", "module")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -202,8 +200,6 @@ Options:
default this will be root. Child modules can be specified
by names. Ex. "consul" or "consul.vpc" (nested modules).
-no-color If specified, output won't contain any color.
-state=path Path to read and save state (unless state-out
is specified). Defaults to "terraform.tfstate".

View File

@ -25,13 +25,18 @@ func (c *ValidateCommand) Run(args []string) int {
return 1
}
var jsonOutput bool
cmdFlags := c.Meta.flagSet("validate")
cmdFlags.BoolVar(&jsonOutput, "json", false, "produce JSON output")
cmdFlags.Usage = func() {
c.Ui.Error(c.Help())
if c.Meta.variableArgs.items == nil {
c.Meta.variableArgs = newRawFlags("-var")
}
varValues := c.Meta.variableArgs.Alias("-var")
varFiles := c.Meta.variableArgs.Alias("-var-file")
var jsonOutput bool
cmdFlags := c.Meta.defaultFlagSet("validate")
cmdFlags.BoolVar(&jsonOutput, "json", false, "produce JSON output")
cmdFlags.Var(varValues, "var", "variables")
cmdFlags.Var(varFiles, "var-file", "variable file")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
@ -68,49 +73,6 @@ func (c *ValidateCommand) Run(args []string) int {
return c.showResults(diags, jsonOutput)
}
func (c *ValidateCommand) Synopsis() string {
return "Validates the Terraform files"
}
func (c *ValidateCommand) Help() string {
helpText := `
Usage: terraform validate [options] [dir]
Validate the configuration files in a directory, referring only to the
configuration and not accessing any remote services such as remote state,
provider APIs, etc.
Validate runs checks that verify whether a configuration is
internally-consistent, regardless of any provided variables or existing
state. It is thus primarily useful for general verification of reusable
modules, including correctness of attribute names and value types.
To verify configuration in the context of a particular run (a particular
target workspace, operation variables, etc), use the following command
instead:
terraform plan -validate-only
It is safe to run this command automatically, for example as a post-save
check in a text editor or as a test step for a re-usable module in a CI
system.
Validation requires an initialized working directory with any referenced
plugins and modules installed. To initialize a working directory for
validation without accessing any configured remote backend, use:
terraform init -backend=false
If dir is not specified, then the current directory will be used.
Options:
-json Produce output in a machine-readable JSON format, suitable for
use in e.g. text editor integrations.
-no-color If specified, output won't contain any color.
`
return strings.TrimSpace(helpText)
}
func (c *ValidateCommand) validate(dir string) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
@ -254,3 +216,45 @@ func (c *ValidateCommand) showResults(diags tfdiags.Diagnostics, jsonOutput bool
}
return 0
}
func (c *ValidateCommand) Synopsis() string {
return "Validates the Terraform files"
}
func (c *ValidateCommand) Help() string {
helpText := `
Usage: terraform validate [options] [dir]
Validate the configuration files in a directory, referring only to the
configuration and not accessing any remote services such as remote state,
provider APIs, etc.
Validate runs checks that verify whether a configuration is
internally-consistent, regardless of any provided variables or existing
state. It is thus primarily useful for general verification of reusable
modules, including correctness of attribute names and value types.
To verify configuration in the context of a particular run (a particular
target workspace, operation variables, etc), use the following command
instead:
terraform plan -validate-only
It is safe to run this command automatically, for example as a post-save
check in a text editor or as a test step for a re-usable module in a CI
system.
Validation requires an initialized working directory with any referenced
plugins and modules installed. To initialize a working directory for
validation without accessing any configured remote backend, use:
terraform init -backend=false
If dir is not specified, then the current directory will be used.
Options:
-json Produce output in a machine-readable JSON format, suitable for
use in e.g. text editor integrations.
`
return strings.TrimSpace(helpText)
}

View File

@ -22,7 +22,7 @@ func (c *WorkspaceCommand) Run(args []string) int {
envCommandShowWarning(c.Ui, c.LegacyName)
cmdFlags := c.Meta.flagSet("workspace")
cmdFlags := c.Meta.extendedFlagSet("workspace")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
c.Ui.Output(c.Help())

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/tfdiags"
@ -24,23 +25,28 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
envCommandShowWarning(c.Ui, c.LegacyName)
force := false
cmdFlags := c.Meta.flagSet("workspace")
var force bool
var stateLock bool
var stateLockTimeout time.Duration
cmdFlags := c.Meta.defaultFlagSet("workspace delete")
cmdFlags.BoolVar(&force, "force", false, "force removal of a non-empty workspace")
cmdFlags.BoolVar(&stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
args = cmdFlags.Args()
if len(args) == 0 {
c.Ui.Error("expected NAME.\n")
return cli.RunResultHelp
}
delEnv := args[0]
workspace := args[0]
if !validWorkspaceName(delEnv) {
c.Ui.Error(fmt.Sprintf(envInvalidName, delEnv))
if !validWorkspaceName(workspace) {
c.Ui.Error(fmt.Sprintf(envInvalidName, workspace))
return 1
}
@ -69,41 +75,41 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
return 1
}
states, err := b.Workspaces()
workspaces, err := b.Workspaces()
if err != nil {
c.Ui.Error(err.Error())
return 1
}
exists := false
for _, s := range states {
if delEnv == s {
for _, ws := range workspaces {
if workspace == ws {
exists = true
break
}
}
if !exists {
c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDoesNotExist), delEnv))
c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDoesNotExist), workspace))
return 1
}
if delEnv == c.Workspace() {
c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDelCurrent), delEnv))
if workspace == c.Workspace() {
c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDelCurrent), workspace))
return 1
}
// we need the actual state to see if it's empty
sMgr, err := b.StateMgr(delEnv)
stateMgr, err := b.StateMgr(workspace)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
var stateLocker clistate.Locker
if c.stateLock {
stateLocker = clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(sMgr, "workspace_delete"); err != nil {
if stateLock {
stateLocker = clistate.NewLocker(context.Background(), stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "workspace_delete"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
return 1
}
@ -111,15 +117,15 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
stateLocker = clistate.NewNoopLocker()
}
if err := sMgr.RefreshState(); err != nil {
if err := stateMgr.RefreshState(); err != nil {
c.Ui.Error(err.Error())
return 1
}
hasResources := sMgr.State().HasResources()
hasResources := stateMgr.State().HasResources()
if hasResources && !force {
c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envNotEmpty), delEnv))
c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envNotEmpty), workspace))
return 1
}
@ -134,7 +140,7 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
// be delegated from the Backend to the State itself.
stateLocker.Unlock(nil)
err = b.DeleteWorkspace(delEnv)
err = b.DeleteWorkspace(workspace)
if err != nil {
c.Ui.Error(err.Error())
return 1
@ -142,14 +148,14 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
c.Ui.Output(
c.Colorize().Color(
fmt.Sprintf(envDeleted, delEnv),
fmt.Sprintf(envDeleted, workspace),
),
)
if hasResources {
c.Ui.Output(
c.Colorize().Color(
fmt.Sprintf(envWarnNotEmpty, delEnv),
fmt.Sprintf(envWarnNotEmpty, workspace),
),
)
}
@ -181,6 +187,11 @@ Usage: terraform workspace delete [OPTIONS] NAME [DIR]
Options:
-force remove a non-empty workspace.
-lock=true Lock the state file when locking is supported.
-lock-timeout=0s Duration to retry a state lock.
`
return strings.TrimSpace(helpText)
}

View File

@ -21,7 +21,7 @@ func (c *WorkspaceListCommand) Run(args []string) int {
envCommandShowWarning(c.Ui, c.LegacyName)
cmdFlags := c.Meta.flagSet("workspace list")
cmdFlags := c.Meta.defaultFlagSet("workspace list")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -93,6 +93,7 @@ func (c *WorkspaceListCommand) Help() string {
Usage: terraform workspace list [DIR]
List Terraform workspaces.
`
return strings.TrimSpace(helpText)
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"strings"
"time"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/states/statefile"
@ -26,30 +27,34 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
envCommandShowWarning(c.Ui, c.LegacyName)
statePath := ""
cmdFlags := c.Meta.flagSet("workspace new")
var stateLock bool
var stateLockTimeout time.Duration
var statePath string
cmdFlags := c.Meta.defaultFlagSet("workspace new")
cmdFlags.BoolVar(&stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.StringVar(&statePath, "state", "", "terraform state file")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
args = cmdFlags.Args()
if len(args) == 0 {
c.Ui.Error("Expected a single argument: NAME.\n")
return cli.RunResultHelp
}
newEnv := args[0]
workspace := args[0]
if !validWorkspaceName(newEnv) {
c.Ui.Error(fmt.Sprintf(envInvalidName, newEnv))
if !validWorkspaceName(workspace) {
c.Ui.Error(fmt.Sprintf(envInvalidName, workspace))
return 1
}
// You can't ask to create a workspace when you're overriding the
// workspace name to be something different.
if current, isOverridden := c.WorkspaceOverridden(); current != newEnv && isOverridden {
if current, isOverridden := c.WorkspaceOverridden(); current != workspace && isOverridden {
c.Ui.Error(envIsOverriddenNewError)
return 1
}
@ -79,32 +84,32 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
return 1
}
states, err := b.Workspaces()
workspaces, err := b.Workspaces()
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to get configured named states: %s", err))
return 1
}
for _, s := range states {
if newEnv == s {
c.Ui.Error(fmt.Sprintf(envExists, newEnv))
for _, ws := range workspaces {
if workspace == ws {
c.Ui.Error(fmt.Sprintf(envExists, workspace))
return 1
}
}
_, err = b.StateMgr(newEnv)
_, err = b.StateMgr(workspace)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
// now set the current workspace locally
if err := c.SetWorkspace(newEnv); err != nil {
if err := c.SetWorkspace(workspace); err != nil {
c.Ui.Error(fmt.Sprintf("Error selecting new workspace: %s", err))
return 1
}
c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
strings.TrimSpace(envCreated), newEnv)))
strings.TrimSpace(envCreated), workspace)))
if statePath == "" {
// if we're not loading a state, then we're done
@ -112,15 +117,15 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
}
// load the new Backend state
sMgr, err := b.StateMgr(newEnv)
stateMgr, err := b.StateMgr(workspace)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
if c.stateLock {
stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(sMgr, "workspace_delete"); err != nil {
if stateLock {
stateLocker := clistate.NewLocker(context.Background(), stateLockTimeout, c.Ui, c.Colorize())
if err := stateLocker.Lock(stateMgr, "workspace_new"); err != nil {
c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
return 1
}
@ -141,12 +146,12 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
}
// save the existing state in the new Backend.
err = sMgr.WriteState(stateFile.State)
err = stateMgr.WriteState(stateFile.State)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
err = sMgr.PersistState()
err = stateMgr.PersistState()
if err != nil {
c.Ui.Error(err.Error())
return 1
@ -178,7 +183,12 @@ Usage: terraform workspace new [OPTIONS] NAME [DIR]
Options:
-lock=true Lock the state file when locking is supported.
-lock-timeout=0s Duration to retry a state lock.
-state=path Copy an existing state file into the new workspace.
`
return strings.TrimSpace(helpText)
}

View File

@ -22,11 +22,12 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
envCommandShowWarning(c.Ui, c.LegacyName)
cmdFlags := c.Meta.flagSet("workspace select")
cmdFlags := c.Meta.defaultFlagSet("workspace select")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
args = cmdFlags.Args()
if len(args) == 0 {
c.Ui.Error("Expected a single argument: NAME.\n")
@ -131,6 +132,7 @@ func (c *WorkspaceSelectCommand) Help() string {
Usage: terraform workspace select NAME [DIR]
Select a different Terraform workspace.
`
return strings.TrimSpace(helpText)
}

View File

@ -16,7 +16,7 @@ func (c *WorkspaceShowCommand) Run(args []string) int {
return 1
}
cmdFlags := c.Meta.flagSet("workspace show")
cmdFlags := c.Meta.extendedFlagSet("workspace show")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1