terraform/command/plan.go

189 lines
5.8 KiB
Go
Raw Normal View History

package command
import (
"fmt"
"strings"
2017-01-19 05:50:45 +01:00
"github.com/hashicorp/terraform/backend"
2021-02-17 19:01:30 +01:00
"github.com/hashicorp/terraform/command/arguments"
"github.com/hashicorp/terraform/command/views"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/terraform/tfdiags"
)
2014-06-20 20:47:02 +02:00
// PlanCommand is a Command implementation that compares a Terraform
// configuration to an actual infrastructure and shows the differences.
2014-06-20 20:47:02 +02:00
type PlanCommand struct {
Meta
}
2014-06-20 20:47:02 +02:00
func (c *PlanCommand) Run(args []string) int {
var destroy, refresh, detailed bool
2014-10-12 03:47:52 +02:00
var outPath string
2014-06-19 22:51:05 +02:00
args = c.Meta.process(args)
cmdFlags := c.Meta.extendedFlagSet("plan")
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
2014-06-26 18:56:29 +02:00
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
2014-06-27 07:23:51 +02:00
cmdFlags.StringVar(&outPath, "out", "", "path")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism")
2017-01-19 05:50:45 +01:00
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
2017-02-06 16:07:32 +01:00
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
return 1
}
diags := c.parseTargetFlags()
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
2017-01-19 05:50:45 +01:00
configPath, err := ModulePath(cmdFlags.Args())
if err != nil {
c.Ui.Error(err.Error())
return 1
}
// Check for user-supplied plugin path
if c.pluginPath, err = c.loadPluginPath(); err != nil {
c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err))
return 1
}
var backendConfig *configs.Backend
var configDiags tfdiags.Diagnostics
backendConfig, configDiags = c.loadBackendConfig(configPath)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
c.showDiagnostics(diags)
return 1
2014-06-26 18:56:29 +02:00
}
2017-01-19 05:50:45 +01:00
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
Config: backendConfig,
2017-01-19 05:50:45 +01:00
})
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)
2014-06-10 20:34:08 +02:00
return 1
}
// Emit any diagnostics we've accumulated before we delegate to the
// backend, since the backend will handle its own diagnostics internally.
c.showDiagnostics(diags)
diags = nil
2017-01-19 05:50:45 +01:00
// Build the operation
opReq := c.Operation(b)
opReq.ConfigDir = configPath
opReq.Destroy = destroy
opReq.Hooks = []terraform.Hook{c.uiHook()}
2017-01-19 05:50:45 +01:00
opReq.PlanOutPath = outPath
opReq.PlanRefresh = refresh
opReq.ShowDiagnostics = c.showDiagnostics
2017-01-19 05:50:45 +01:00
opReq.Type = backend.OperationTypePlan
2021-02-17 19:01:30 +01:00
opReq.View = views.NewOperation(arguments.ViewHuman, c.RunningInAutomation, c.View)
opReq.ConfigLoader, err = c.initConfigLoader()
if err != nil {
c.showDiagnostics(err)
return 1
}
{
var moreDiags tfdiags.Diagnostics
opReq.Variables, moreDiags = c.collectVariableValues()
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
}
2017-01-19 05:50:45 +01:00
// Perform the operation
op, err := c.RunOperation(b, opReq)
2017-01-19 05:50:45 +01:00
if err != nil {
c.showDiagnostics(err)
return 1
}
if op.Result != backend.OperationSuccess {
return op.Result.ExitStatus()
}
2017-01-19 05:50:45 +01:00
if detailed && !op.PlanEmpty {
return 2
}
2017-01-19 05:50:45 +01:00
return op.Result.ExitStatus()
}
2014-06-20 20:47:02 +02:00
func (c *PlanCommand) Help() string {
helpText := `
Usage: terraform plan [options]
Generates a speculative execution plan, showing what actions Terraform
would take to apply the current configuration. This command will not
actually perform the planned actions.
You can optionally save the plan to a file, which you can then pass to
the "apply" command to perform exactly the actions described in the plan.
Options:
-compact-warnings If Terraform produces any warnings that are not
accompanied by errors, show them in a more compact form
that includes only the summary messages.
2014-07-01 18:12:35 +02:00
-destroy If set, a plan will be generated to destroy all resources
managed by the given configuration and state.
-detailed-exitcode Return detailed exit codes when the command exits. This
will change the meaning of exit codes to:
0 - Succeeded, diff is empty (no changes)
1 - Errored
2 - Succeeded, there is a diff
-input=true Ask for input for variables if not directly set.
2017-02-06 16:07:32 +01:00
-lock=true Lock the state file when locking is supported.
-lock-timeout=0s Duration to retry a state lock.
-no-color If specified, output won't contain any color.
2014-06-27 07:23:51 +02:00
-out=path Write a plan file to the given path. This can be used as
input to the "apply" command.
-parallelism=n Limit the number of concurrent operations. Defaults to 10.
2014-06-26 18:56:29 +02:00
-refresh=true Update state prior to checking for differences.
-state=statefile Path to a Terraform state file to use to look
up Terraform-managed resources. By default it will
use the state "terraform.tfstate" if it exists.
-target=resource Resource to target. Operation will be limited to this
resource and its dependencies. This flag can be used
multiple times.
2014-07-18 20:37:27 +02:00
-var 'foo=bar' Set a variable in the Terraform configuration. This
flag can be set multiple times.
-var-file=foo Set variables in the Terraform configuration from
2017-06-22 03:22:07 +02:00
a file. If "terraform.tfvars" or any ".auto.tfvars"
files are present, they will be automatically loaded.
`
return strings.TrimSpace(helpText)
}
2014-06-20 20:47:02 +02:00
func (c *PlanCommand) Synopsis() string {
return "Show changes required by the current configuration"
}