mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge #7251: terraform apply -auto-approve=false
This commit is contained in:
commit
c487c0bd0e
@ -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
|
||||||
|
@ -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.
|
||||||
|
`
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user