terraform/command/plan.go

182 lines
5.2 KiB
Go
Raw Normal View History

package command
import (
2017-01-19 05:50:45 +01:00
"context"
"fmt"
"strings"
2017-01-19 05:50:45 +01:00
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/config/module"
)
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
var moduleDepth int
2014-06-19 22:51:05 +02:00
args = c.Meta.process(args, true)
2014-07-18 20:37:27 +02:00
cmdFlags := c.Meta.flagSet("plan")
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
2014-06-26 18:56:29 +02:00
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
c.addModuleDepthFlag(cmdFlags, &moduleDepth)
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")
cmdFlags.BoolVar(&c.Meta.lockState, "state-lock", true, "lock state")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
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
}
2017-01-19 05:50:45 +01:00
// Check if the path is a plan
plan, err := c.Plan(configPath)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
2017-01-19 05:50:45 +01:00
if plan != nil {
// Disable refreshing no matter what since we only want to show the plan
refresh = false
2017-01-19 05:50:45 +01:00
// Set the config path to empty for backend loading
configPath = ""
}
2017-01-19 05:50:45 +01:00
// Load the module if we don't have one yet (not running from plan)
var mod *module.Tree
if plan == nil {
mod, err = c.Module(configPath)
2014-10-12 03:47:52 +02:00
if err != nil {
2017-01-19 05:50:45 +01:00
c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
2014-06-26 18:56:29 +02:00
return 1
}
}
2017-01-19 05:50:45 +01:00
// Load the backend
b, err := c.Backend(&BackendOpts{
ConfigPath: configPath,
Plan: plan,
})
2014-06-10 20:34:08 +02:00
if err != nil {
2017-01-19 05:50:45 +01:00
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
2014-06-10 20:34:08 +02:00
return 1
}
2017-01-19 05:50:45 +01:00
// Build the operation
opReq := c.Operation()
opReq.Destroy = destroy
opReq.Module = mod
opReq.Plan = plan
opReq.PlanRefresh = refresh
opReq.PlanOutPath = outPath
opReq.Type = backend.OperationTypePlan
opReq.LockState = c.Meta.lockState
2017-01-19 05:50:45 +01:00
// Perform the operation
op, err := b.Operation(context.Background(), opReq)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err))
return 1
2014-07-13 01:32:48 +02:00
}
2017-01-19 05:50:45 +01:00
// Wait for the operation to complete
<-op.Done()
if err := op.Err; err != nil {
c.Ui.Error(err.Error())
return 1
}
2017-01-19 05:50:45 +01:00
/*
err = terraform.SetDebugInfo(DefaultDataDir)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
*/
2017-01-19 05:50:45 +01:00
if detailed && !op.PlanEmpty {
return 2
}
2017-01-19 05:50:45 +01:00
return 0
}
2014-06-20 20:47:02 +02:00
func (c *PlanCommand) Help() string {
helpText := `
Usage: terraform plan [options] [DIR-OR-PLAN]
Generates an execution plan for Terraform.
This execution plan can be reviewed prior to running apply to get a
sense for what Terraform will do. Optionally, the plan can be saved to
a Terraform plan file, and apply can take this plan file to execute
this plan exactly.
If a saved plan is passed as an argument, this command will output
the saved plan contents. It will not modify the given plan.
Options:
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.
-lock-state=true Lock the state file when locking is supported.
-module-depth=n Specifies the depth of modules to show in the output.
This does not affect the plan itself, only the output
shown. By default, this is -1, which will expand all.
-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
a file. If "terraform.tfvars" is present, it will be
automatically loaded if this flag is not specified.
`
return strings.TrimSpace(helpText)
}
2014-06-20 20:47:02 +02:00
func (c *PlanCommand) Synopsis() string {
2014-07-13 04:28:38 +02:00
return "Generate and show an execution plan"
}