github.com/gerbenjacobs/terraform@v0.9.5-0.20170630130047-e6ddd62583d8/command/plan.go (about) 1 package command 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/hashicorp/terraform/backend" 9 "github.com/hashicorp/terraform/config" 10 "github.com/hashicorp/terraform/config/module" 11 ) 12 13 // PlanCommand is a Command implementation that compares a Terraform 14 // configuration to an actual infrastructure and shows the differences. 15 type PlanCommand struct { 16 Meta 17 } 18 19 func (c *PlanCommand) Run(args []string) int { 20 var destroy, refresh, detailed bool 21 var outPath string 22 var moduleDepth int 23 24 args = c.Meta.process(args, true) 25 26 cmdFlags := c.Meta.flagSet("plan") 27 cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") 28 cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") 29 c.addModuleDepthFlag(cmdFlags, &moduleDepth) 30 cmdFlags.StringVar(&outPath, "out", "", "path") 31 cmdFlags.IntVar( 32 &c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") 33 cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") 34 cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode") 35 cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") 36 cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") 37 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 38 if err := cmdFlags.Parse(args); err != nil { 39 return 1 40 } 41 42 configPath, err := ModulePath(cmdFlags.Args()) 43 if err != nil { 44 c.Ui.Error(err.Error()) 45 return 1 46 } 47 48 // Check for user-supplied plugin path 49 if c.pluginPath, err = c.loadPluginPath(); err != nil { 50 c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) 51 return 1 52 } 53 54 // Check if the path is a plan 55 plan, err := c.Plan(configPath) 56 if err != nil { 57 c.Ui.Error(err.Error()) 58 return 1 59 } 60 if plan != nil { 61 // Disable refreshing no matter what since we only want to show the plan 62 refresh = false 63 64 // Set the config path to empty for backend loading 65 configPath = "" 66 } 67 68 // Load the module if we don't have one yet (not running from plan) 69 var mod *module.Tree 70 if plan == nil { 71 mod, err = c.Module(configPath) 72 if err != nil { 73 c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err)) 74 return 1 75 } 76 } 77 78 var conf *config.Config 79 if mod != nil { 80 conf = mod.Config() 81 } 82 // Load the backend 83 b, err := c.Backend(&BackendOpts{ 84 Config: conf, 85 Plan: plan, 86 }) 87 if err != nil { 88 c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) 89 return 1 90 } 91 92 // Build the operation 93 opReq := c.Operation() 94 opReq.Destroy = destroy 95 opReq.Module = mod 96 opReq.Plan = plan 97 opReq.PlanRefresh = refresh 98 opReq.PlanOutPath = outPath 99 opReq.Type = backend.OperationTypePlan 100 101 // Perform the operation 102 op, err := b.Operation(context.Background(), opReq) 103 if err != nil { 104 c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err)) 105 return 1 106 } 107 108 // Wait for the operation to complete 109 <-op.Done() 110 if err := op.Err; err != nil { 111 c.Ui.Error(err.Error()) 112 return 1 113 } 114 115 /* 116 err = terraform.SetDebugInfo(DefaultDataDir) 117 if err != nil { 118 c.Ui.Error(err.Error()) 119 return 1 120 } 121 */ 122 123 if detailed && !op.PlanEmpty { 124 return 2 125 } 126 127 return 0 128 } 129 130 func (c *PlanCommand) Help() string { 131 helpText := ` 132 Usage: terraform plan [options] [DIR-OR-PLAN] 133 134 Generates an execution plan for Terraform. 135 136 This execution plan can be reviewed prior to running apply to get a 137 sense for what Terraform will do. Optionally, the plan can be saved to 138 a Terraform plan file, and apply can take this plan file to execute 139 this plan exactly. 140 141 If a saved plan is passed as an argument, this command will output 142 the saved plan contents. It will not modify the given plan. 143 144 Options: 145 146 -destroy If set, a plan will be generated to destroy all resources 147 managed by the given configuration and state. 148 149 -detailed-exitcode Return detailed exit codes when the command exits. This 150 will change the meaning of exit codes to: 151 0 - Succeeded, diff is empty (no changes) 152 1 - Errored 153 2 - Succeeded, there is a diff 154 155 -input=true Ask for input for variables if not directly set. 156 157 -lock=true Lock the state file when locking is supported. 158 159 -lock-timeout=0s Duration to retry a state lock. 160 161 -module-depth=n Specifies the depth of modules to show in the output. 162 This does not affect the plan itself, only the output 163 shown. By default, this is -1, which will expand all. 164 165 -no-color If specified, output won't contain any color. 166 167 -out=path Write a plan file to the given path. This can be used as 168 input to the "apply" command. 169 170 -parallelism=n Limit the number of concurrent operations. Defaults to 10. 171 172 -refresh=true Update state prior to checking for differences. 173 174 -state=statefile Path to a Terraform state file to use to look 175 up Terraform-managed resources. By default it will 176 use the state "terraform.tfstate" if it exists. 177 178 -target=resource Resource to target. Operation will be limited to this 179 resource and its dependencies. This flag can be used 180 multiple times. 181 182 -var 'foo=bar' Set a variable in the Terraform configuration. This 183 flag can be set multiple times. 184 185 -var-file=foo Set variables in the Terraform configuration from 186 a file. If "terraform.tfvars" is present, it will be 187 automatically loaded if this flag is not specified. 188 ` 189 return strings.TrimSpace(helpText) 190 } 191 192 func (c *PlanCommand) Synopsis() string { 193 return "Generate and show an execution plan" 194 }