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