command: add "apply -auto-approve=false" flag

A common reason to want to use `terraform plan` is to have a chance to
review and confirm a plan before running it.  If in fact that is the
only reason you are running plan, this new `terraform apply -auto-approve=false`
flag provides an easier alternative to

    P=$(mktemp -t plan)
    terraform refresh
    terraform plan -refresh=false -out=$P
    terraform apply $P
    rm $P

The flag defaults to true for now, but in a future version of Terraform it will
default to false.
This commit is contained in:
David Glasser 2016-06-20 18:06:28 -07:00 committed by Martin Atkins
parent e7f7395e9e
commit 039d36bf91
5 changed files with 59 additions and 5 deletions

View File

@ -121,9 +121,10 @@ type Operation struct {
// The options below are more self-explanatory and affect the runtime
// behavior of the operation.
Destroy bool
Targets []string
Variables map[string]interface{}
Destroy bool
Targets []string
Variables map[string]interface{}
AutoApprove bool
// Input/output/control options.
UIIn terraform.UIInput

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/format"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform"
@ -89,10 +90,37 @@ func (b *Local) opApply(
// Perform the plan
log.Printf("[INFO] backend/local: apply calling Plan")
if _, err := tfCtx.Plan(); err != nil {
plan, err := tfCtx.Plan()
if err != nil {
runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", err)
return
}
trivialPlan := plan.Diff == nil || plan.Diff.Empty()
hasUI := op.UIOut != nil && op.UIIn != nil
if hasUI && !op.AutoApprove && !trivialPlan {
op.UIOut.Output(strings.TrimSpace(approvePlanHeader) + "\n")
op.UIOut.Output(format.Plan(&format.PlanOpts{
Plan: plan,
Color: b.Colorize(),
ModuleDepth: -1,
}))
desc := "Terraform will apply the plan described above.\n" +
"Only 'yes' will be accepted to approve."
v, err := op.UIIn.Input(&terraform.InputOpts{
Id: "approve",
Query: "Do you want to apply the plan above?",
Description: desc,
})
if err != nil {
runningOp.Err = errwrap.Wrapf("Error asking for approval: {{err}}", err)
return
}
if v != "yes" {
runningOp.Err = errors.New("Apply cancelled.")
return
}
}
}
// Setup our hook for continuous state updates
@ -288,3 +316,11 @@ Terraform encountered an error attempting to save the state before canceling
the current operation. Once the operation is complete another attempt will be
made to save the final state.
`
const approvePlanHeader = `
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.
`

View File

@ -29,7 +29,7 @@ type ApplyCommand struct {
}
func (c *ApplyCommand) Run(args []string) int {
var destroyForce, refresh bool
var destroyForce, refresh, autoApprove bool
args = c.Meta.process(args, true)
cmdName := "apply"
@ -42,6 +42,9 @@ func (c *ApplyCommand) Run(args []string) int {
cmdFlags.BoolVar(&destroyForce, "force", false, "force")
}
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
if !c.Destroy {
cmdFlags.BoolVar(&autoApprove, "auto-approve", true, "skip interactive approval of plan before applying")
}
cmdFlags.IntVar(
&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
@ -112,6 +115,11 @@ func (c *ApplyCommand) Run(args []string) int {
if plan != nil {
// Reset the config path for backend loading
configPath = ""
if !autoApprove {
c.Ui.Error("Cannot combine -auto-approve=false with a plan file.")
return 1
}
}
// Load the module if we don't have one yet (not running from plan)
@ -195,6 +203,7 @@ func (c *ApplyCommand) Run(args []string) int {
opReq.Plan = plan
opReq.PlanRefresh = refresh
opReq.Type = backend.OperationTypeApply
opReq.AutoApprove = autoApprove
// Perform the operation
ctx, ctxCancel := context.WithCancel(context.Background())
@ -289,6 +298,10 @@ Options:
-lock-timeout=0s Duration to retry a state lock.
-auto-approve=true Skip interactive approval of plan before applying. In a
future version of Terraform, this flag's default value
will change to false.
-input=true Ask for input for variables if not directly set.
-no-color If specified, output won't contain any color.

View File

@ -168,6 +168,7 @@ func (m *Meta) Operation() *backend.Operation {
PlanOutBackend: m.backendState,
Targets: m.targets,
UIIn: m.UIInput(),
UIOut: m.Ui,
Workspace: m.Workspace(),
LockState: m.stateLock,
StateLockTimeout: m.stateLockTimeout,

View File

@ -37,6 +37,9 @@ The command-line flags are all optional. The list of available flags are:
* `-input=true` - Ask for input for variables if not directly set.
* `-auto-approve=true` - Skip interactive approval of plan before applying. In a
future version of Terraform, this flag's default value will change to false.
* `-no-color` - Disables output with coloring.
* `-parallelism=n` - Limit the number of concurrent operation as Terraform