github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/command/plan.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/muratcelep/terraform/not-internal/backend" 8 "github.com/muratcelep/terraform/not-internal/command/arguments" 9 "github.com/muratcelep/terraform/not-internal/command/views" 10 "github.com/muratcelep/terraform/not-internal/tfdiags" 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(rawArgs []string) int { 20 // Parse and apply global view arguments 21 common, rawArgs := arguments.ParseView(rawArgs) 22 c.View.Configure(common) 23 24 // Propagate -no-color for legacy use of Ui. The remote backend and 25 // cloud package use this; it should be removed when/if they are 26 // migrated to views. 27 c.Meta.color = !common.NoColor 28 c.Meta.Color = c.Meta.color 29 30 // Parse and validate flags 31 args, diags := arguments.ParsePlan(rawArgs) 32 33 // Instantiate the view, even if there are flag errors, so that we render 34 // diagnostics according to the desired view 35 view := views.NewPlan(args.ViewType, c.View) 36 37 if diags.HasErrors() { 38 view.Diagnostics(diags) 39 view.HelpPrompt() 40 return 1 41 } 42 43 // Check for user-supplied plugin path 44 var err error 45 if c.pluginPath, err = c.loadPluginPath(); err != nil { 46 diags = diags.Append(err) 47 view.Diagnostics(diags) 48 return 1 49 } 50 51 // FIXME: the -input flag value is needed to initialize the backend and the 52 // operation, but there is no clear path to pass this value down, so we 53 // continue to mutate the Meta object state for now. 54 c.Meta.input = args.InputEnabled 55 56 // FIXME: the -parallelism flag is used to control the concurrency of 57 // Terraform operations. At the moment, this value is used both to 58 // initialize the backend via the ContextOpts field inside CLIOpts, and to 59 // set a largely unused field on the Operation request. Again, there is no 60 // clear path to pass this value down, so we continue to mutate the Meta 61 // object state for now. 62 c.Meta.parallelism = args.Operation.Parallelism 63 64 diags = diags.Append(c.providerDevOverrideRuntimeWarnings()) 65 66 // Prepare the backend with the backend-specific arguments 67 be, beDiags := c.PrepareBackend(args.State) 68 diags = diags.Append(beDiags) 69 if diags.HasErrors() { 70 view.Diagnostics(diags) 71 return 1 72 } 73 74 // Build the operation request 75 opReq, opDiags := c.OperationRequest(be, view, args.Operation, args.OutPath) 76 diags = diags.Append(opDiags) 77 if diags.HasErrors() { 78 view.Diagnostics(diags) 79 return 1 80 } 81 82 // Collect variable value and add them to the operation request 83 diags = diags.Append(c.GatherVariables(opReq, args.Vars)) 84 if diags.HasErrors() { 85 view.Diagnostics(diags) 86 return 1 87 } 88 89 // Before we delegate to the backend, we'll print any warning diagnostics 90 // we've accumulated here, since the backend will start fresh with its own 91 // diagnostics. 92 view.Diagnostics(diags) 93 diags = nil 94 95 // Perform the operation 96 op, err := c.RunOperation(be, opReq) 97 if err != nil { 98 diags = diags.Append(err) 99 view.Diagnostics(diags) 100 return 1 101 } 102 103 if op.Result != backend.OperationSuccess { 104 return op.Result.ExitStatus() 105 } 106 if args.DetailedExitCode && !op.PlanEmpty { 107 return 2 108 } 109 110 return op.Result.ExitStatus() 111 } 112 113 func (c *PlanCommand) PrepareBackend(args *arguments.State) (backend.Enhanced, tfdiags.Diagnostics) { 114 // FIXME: we need to apply the state arguments to the meta object here 115 // because they are later used when initializing the backend. Carving a 116 // path to pass these arguments to the functions that need them is 117 // difficult but would make their use easier to understand. 118 c.Meta.applyStateArguments(args) 119 120 backendConfig, diags := c.loadBackendConfig(".") 121 if diags.HasErrors() { 122 return nil, diags 123 } 124 125 // Load the backend 126 be, beDiags := c.Backend(&BackendOpts{ 127 Config: backendConfig, 128 }) 129 diags = diags.Append(beDiags) 130 if beDiags.HasErrors() { 131 return nil, diags 132 } 133 134 return be, diags 135 } 136 137 func (c *PlanCommand) OperationRequest( 138 be backend.Enhanced, 139 view views.Plan, 140 args *arguments.Operation, 141 planOutPath string, 142 ) (*backend.Operation, tfdiags.Diagnostics) { 143 var diags tfdiags.Diagnostics 144 145 // Build the operation 146 opReq := c.Operation(be) 147 opReq.ConfigDir = "." 148 opReq.PlanMode = args.PlanMode 149 opReq.Hooks = view.Hooks() 150 opReq.PlanRefresh = args.Refresh 151 opReq.PlanOutPath = planOutPath 152 opReq.Targets = args.Targets 153 opReq.ForceReplace = args.ForceReplace 154 opReq.Type = backend.OperationTypePlan 155 opReq.View = view.Operation() 156 157 var err error 158 opReq.ConfigLoader, err = c.initConfigLoader() 159 if err != nil { 160 diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %s", err)) 161 return nil, diags 162 } 163 164 return opReq, diags 165 } 166 167 func (c *PlanCommand) GatherVariables(opReq *backend.Operation, args *arguments.Vars) tfdiags.Diagnostics { 168 var diags tfdiags.Diagnostics 169 170 // FIXME the arguments package currently trivially gathers variable related 171 // arguments in a heterogenous slice, in order to minimize the number of 172 // code paths gathering variables during the transition to this structure. 173 // Once all commands that gather variables have been converted to this 174 // structure, we could move the variable gathering code to the arguments 175 // package directly, removing this shim layer. 176 177 varArgs := args.All() 178 items := make([]rawFlag, len(varArgs)) 179 for i := range varArgs { 180 items[i].Name = varArgs[i].Name 181 items[i].Value = varArgs[i].Value 182 } 183 c.Meta.variableArgs = rawFlags{items: &items} 184 opReq.Variables, diags = c.collectVariableValues() 185 186 return diags 187 } 188 189 func (c *PlanCommand) Help() string { 190 helpText := ` 191 Usage: terraform [global options] plan [options] 192 193 Generates a speculative execution plan, showing what actions Terraform 194 would take to apply the current configuration. This command will not 195 actually perform the planned actions. 196 197 You can optionally save the plan to a file, which you can then pass to 198 the "apply" command to perform exactly the actions described in the plan. 199 200 Plan Customization Options: 201 202 The following options customize how Terraform will produce its plan. You 203 can also use these options when you run "terraform apply" without passing 204 it a saved plan, in order to plan and apply in a single command. 205 206 -destroy Select the "destroy" planning mode, which creates a plan 207 to destroy all objects currently managed by this 208 Terraform configuration instead of the usual behavior. 209 210 -refresh-only Select the "refresh only" planning mode, which checks 211 whether remote objects still match the outcome of the 212 most recent Terraform apply but does not propose any 213 actions to undo any changes made outside of Terraform. 214 215 -refresh=false Skip checking for external changes to remote objects 216 while creating the plan. This can potentially make 217 planning faster, but at the expense of possibly planning 218 against a stale record of the remote system state. 219 220 -replace=resource Force replacement of a particular resource instance using 221 its resource address. If the plan would've normally 222 produced an update or no-op action for this instance, 223 Terraform will plan to replace it instead. 224 225 -target=resource Limit the planning operation to only the given module, 226 resource, or resource instance and all of its 227 dependencies. You can use this option multiple times to 228 include more than one object. This is for exceptional 229 use only. 230 231 -var 'foo=bar' Set a value for one of the input variables in the root 232 module of the configuration. Use this option more than 233 once to set more than one variable. 234 235 -var-file=filename Load variable values from the given file, in addition 236 to the default files terraform.tfvars and *.auto.tfvars. 237 Use this option more than once to include more than one 238 variables file. 239 240 Other Options: 241 242 -compact-warnings If Terraform produces any warnings that are not 243 accompanied by errors, shows them in a more compact form 244 that includes only the summary messages. 245 246 -detailed-exitcode Return detailed exit codes when the command exits. This 247 will change the meaning of exit codes to: 248 0 - Succeeded, diff is empty (no changes) 249 1 - Errored 250 2 - Succeeded, there is a diff 251 252 -input=true Ask for input for variables if not directly set. 253 254 -lock=false Don't hold a state lock during the operation. This is 255 dangerous if others might concurrently run commands 256 against the same workspace. 257 258 -lock-timeout=0s Duration to retry a state lock. 259 260 -no-color If specified, output won't contain any color. 261 262 -out=path Write a plan file to the given path. This can be used as 263 input to the "apply" command. 264 265 -parallelism=n Limit the number of concurrent operations. Defaults to 10. 266 267 -state=statefile A legacy option used for the local backend only. See the 268 local backend's documentation for more information. 269 ` 270 return strings.TrimSpace(helpText) 271 } 272 273 func (c *PlanCommand) Synopsis() string { 274 return "Show changes required by the current configuration" 275 }