Merge #7251: terraform apply -auto-approve=false

This commit is contained in:
Martin Atkins 2017-06-27 11:23:47 -07:00
commit c487c0bd0e
5 changed files with 103 additions and 40 deletions

View File

@ -124,6 +124,8 @@ type Operation struct {
Destroy bool Destroy bool
Targets []string Targets []string
Variables map[string]interface{} Variables map[string]interface{}
AutoApprove bool
DestroyForce 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,73 @@ 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.Destroy && !op.DestroyForce) ||
(!op.Destroy && !op.AutoApprove && !trivialPlan)) {
var desc, query string
if op.Destroy {
// Default destroy message
desc = "Terraform will delete all your managed infrastructure, as shown above.\n" +
"There is no undo. Only 'yes' will be accepted to confirm."
// If targets are specified, list those to user
if op.Targets != nil {
var descBuffer bytes.Buffer
descBuffer.WriteString("Terraform will delete the following infrastructure:\n")
for _, target := range op.Targets {
descBuffer.WriteString("\t")
descBuffer.WriteString(target)
descBuffer.WriteString("\n")
}
descBuffer.WriteString("There is no undo. Only 'yes' will be accepted to confirm")
desc = descBuffer.String()
}
query = "Do you really want to destroy?"
} else {
desc = "Terraform will apply the changes described above.\n" +
"Only 'yes' will be accepted to approve."
query = "Do you want to apply these changes?"
}
if !trivialPlan {
// Display the plan of what we are going to apply/destroy.
if op.Destroy {
op.UIOut.Output("\n" + strings.TrimSpace(approveDestroyPlanHeader) + "\n")
} else {
op.UIOut.Output("\n" + strings.TrimSpace(approvePlanHeader) + "\n")
}
op.UIOut.Output(format.Plan(&format.PlanOpts{
Plan: plan,
Color: b.Colorize(),
ModuleDepth: -1,
}))
}
v, err := op.UIIn.Input(&terraform.InputOpts{
Id: "approve",
Query: query,
Description: desc,
})
if err != nil {
runningOp.Err = errwrap.Wrapf("Error asking for approval: {{err}}", err)
return
}
if v != "yes" {
if op.Destroy {
runningOp.Err = errors.New("Destroy cancelled.")
} else {
runningOp.Err = errors.New("Apply cancelled.")
}
return
}
}
} }
// Setup our hook for continuous state updates // Setup our hook for continuous state updates
@ -288,3 +352,17 @@ 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.
`
const approveDestroyPlanHeader = `
The Terraform destroy plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning.
Resources shown in red will be destroyed.
`

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)
@ -153,41 +161,6 @@ func (c *ApplyCommand) Run(args []string) int {
return 1 return 1
} }
// If we're not forcing and we're destroying, verify with the
// user at this point.
if !destroyForce && c.Destroy {
// Default destroy message
desc := "Terraform will delete all your managed infrastructure.\n" +
"There is no undo. Only 'yes' will be accepted to confirm."
// If targets are specified, list those to user
if c.Meta.targets != nil {
var descBuffer bytes.Buffer
descBuffer.WriteString("Terraform will delete the following infrastructure:\n")
for _, target := range c.Meta.targets {
descBuffer.WriteString("\t")
descBuffer.WriteString(target)
descBuffer.WriteString("\n")
}
descBuffer.WriteString("There is no undo. Only 'yes' will be accepted to confirm")
desc = descBuffer.String()
}
v, err := c.UIInput().Input(&terraform.InputOpts{
Id: "destroy",
Query: "Do you really want to destroy?",
Description: desc,
})
if err != nil {
c.Ui.Error(fmt.Sprintf("Error asking for confirmation: %s", err))
return 1
}
if v != "yes" {
c.Ui.Output("Destroy cancelled.")
return 1
}
}
// Build the operation // Build the operation
opReq := c.Operation() opReq := c.Operation()
opReq.Destroy = c.Destroy opReq.Destroy = c.Destroy
@ -195,6 +168,8 @@ 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
opReq.DestroyForce = destroyForce
// Perform the operation // Perform the operation
ctx, ctxCancel := context.WithCancel(context.Background()) ctx, ctxCancel := context.WithCancel(context.Background())
@ -289,6 +264,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