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 // The options below are more self-explanatory and affect the runtime
// behavior of the operation. // behavior of the operation.
Destroy bool Destroy bool
Targets []string Targets []string
Variables map[string]interface{} Variables map[string]interface{}
AutoApprove bool
// Input/output/control options. // Input/output/control options.
UIIn terraform.UIInput UIIn terraform.UIInput

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate" "github.com/hashicorp/terraform/command/clistate"
"github.com/hashicorp/terraform/command/format"
"github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/state" "github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
@ -89,10 +90,37 @@ func (b *Local) opApply(
// Perform the plan // Perform the plan
log.Printf("[INFO] backend/local: apply calling 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) runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", err)
return 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 // 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 the current operation. Once the operation is complete another attempt will be
made to save the final state. 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 { func (c *ApplyCommand) Run(args []string) int {
var destroyForce, refresh bool var destroyForce, refresh, autoApprove bool
args = c.Meta.process(args, true) args = c.Meta.process(args, true)
cmdName := "apply" cmdName := "apply"
@ -42,6 +42,9 @@ func (c *ApplyCommand) Run(args []string) int {
cmdFlags.BoolVar(&destroyForce, "force", false, "force") cmdFlags.BoolVar(&destroyForce, "force", false, "force")
} }
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
if !c.Destroy {
cmdFlags.BoolVar(&autoApprove, "auto-approve", true, "skip interactive approval of plan before applying")
}
cmdFlags.IntVar( cmdFlags.IntVar(
&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") &c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
@ -112,6 +115,11 @@ func (c *ApplyCommand) Run(args []string) int {
if plan != nil { if plan != nil {
// Reset the config path for backend loading // Reset the config path for backend loading
configPath = "" 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) // 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.Plan = plan
opReq.PlanRefresh = refresh opReq.PlanRefresh = refresh
opReq.Type = backend.OperationTypeApply opReq.Type = backend.OperationTypeApply
opReq.AutoApprove = autoApprove
// Perform the operation // Perform the operation
ctx, ctxCancel := context.WithCancel(context.Background()) ctx, ctxCancel := context.WithCancel(context.Background())
@ -289,6 +298,10 @@ Options:
-lock-timeout=0s Duration to retry a state lock. -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. -input=true Ask for input for variables if not directly set.
-no-color If specified, output won't contain any color. -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, PlanOutBackend: m.backendState,
Targets: m.targets, Targets: m.targets,
UIIn: m.UIInput(), UIIn: m.UIInput(),
UIOut: m.Ui,
Workspace: m.Workspace(), Workspace: m.Workspace(),
LockState: m.stateLock, LockState: m.stateLock,
StateLockTimeout: m.stateLockTimeout, 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. * `-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. * `-no-color` - Disables output with coloring.
* `-parallelism=n` - Limit the number of concurrent operation as Terraform * `-parallelism=n` - Limit the number of concurrent operation as Terraform