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