github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/context_apply.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform/internal/addrs" 8 "github.com/hashicorp/terraform/internal/configs" 9 "github.com/hashicorp/terraform/internal/plans" 10 "github.com/hashicorp/terraform/internal/states" 11 "github.com/hashicorp/terraform/internal/tfdiags" 12 "github.com/zclconf/go-cty/cty" 13 ) 14 15 // Apply performs the actions described by the given Plan object and returns 16 // the resulting updated state. 17 // 18 // The given configuration *must* be the same configuration that was passed 19 // earlier to Context.Plan in order to create this plan. 20 // 21 // Even if the returned diagnostics contains errors, Apply always returns the 22 // resulting state which is likely to have been partially-updated. 23 func (c *Context) Apply(plan *plans.Plan, config *configs.Config) (*states.State, tfdiags.Diagnostics) { 24 defer c.acquireRun("apply")() 25 26 log.Printf("[DEBUG] Building and walking apply graph for %s plan", plan.UIMode) 27 28 graph, operation, diags := c.applyGraph(plan, config, true) 29 if diags.HasErrors() { 30 return nil, diags 31 } 32 33 workingState := plan.PriorState.DeepCopy() 34 walker, walkDiags := c.walk(graph, operation, &graphWalkOpts{ 35 Config: config, 36 InputState: workingState, 37 Changes: plan.Changes, 38 39 // We need to propagate the check results from the plan phase, 40 // because that will tell us which checkable objects we're expecting 41 // to see updated results from during the apply step. 42 PlanTimeCheckResults: plan.Checks, 43 }) 44 diags = diags.Append(walker.NonFatalDiagnostics) 45 diags = diags.Append(walkDiags) 46 47 // After the walk is finished, we capture a simplified snapshot of the 48 // check result data as part of the new state. 49 walker.State.RecordCheckResults(walker.Checks) 50 51 newState := walker.State.Close() 52 if plan.UIMode == plans.DestroyMode && !diags.HasErrors() { 53 // NOTE: This is a vestigial violation of the rule that we mustn't 54 // use plan.UIMode to affect apply-time behavior. 55 // We ideally ought to just call newState.PruneResourceHusks 56 // unconditionally here, but we historically didn't and haven't yet 57 // verified that it'd be safe to do so. 58 newState.PruneResourceHusks() 59 } 60 61 if len(plan.TargetAddrs) > 0 { 62 diags = diags.Append(tfdiags.Sourceless( 63 tfdiags.Warning, 64 "Applied changes may be incomplete", 65 `The plan was created with the -target option in effect, so some changes requested in the configuration may have been ignored and the output values may not be fully updated. Run the following command to verify that no other changes are pending: 66 terraform plan 67 68 Note that the -target option is not suitable for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform specifically suggests to use it as part of an error message.`, 69 )) 70 } 71 72 return newState, diags 73 } 74 75 func (c *Context) applyGraph(plan *plans.Plan, config *configs.Config, validate bool) (*Graph, walkOperation, tfdiags.Diagnostics) { 76 var diags tfdiags.Diagnostics 77 78 variables := InputValues{} 79 for name, dyVal := range plan.VariableValues { 80 val, err := dyVal.Decode(cty.DynamicPseudoType) 81 if err != nil { 82 diags = diags.Append(tfdiags.Sourceless( 83 tfdiags.Error, 84 "Invalid variable value in plan", 85 fmt.Sprintf("Invalid value for variable %q recorded in plan file: %s.", name, err), 86 )) 87 continue 88 } 89 90 variables[name] = &InputValue{ 91 Value: val, 92 SourceType: ValueFromPlan, 93 } 94 } 95 if diags.HasErrors() { 96 return nil, walkApply, diags 97 } 98 99 // The plan.VariableValues field only records variables that were actually 100 // set by the caller in the PlanOpts, so we may need to provide 101 // placeholders for any other variables that the user didn't set, in 102 // which case Terraform will once again use the default value from the 103 // configuration when we visit these variables during the graph walk. 104 for name := range config.Module.Variables { 105 if _, ok := variables[name]; ok { 106 continue 107 } 108 variables[name] = &InputValue{ 109 Value: cty.NilVal, 110 SourceType: ValueFromPlan, 111 } 112 } 113 114 graph, moreDiags := (&ApplyGraphBuilder{ 115 Config: config, 116 Changes: plan.Changes, 117 State: plan.PriorState, 118 RootVariableValues: variables, 119 Plugins: c.plugins, 120 Targets: plan.TargetAddrs, 121 ForceReplace: plan.ForceReplaceAddrs, 122 }).Build(addrs.RootModuleInstance) 123 diags = diags.Append(moreDiags) 124 if moreDiags.HasErrors() { 125 return nil, walkApply, diags 126 } 127 128 operation := walkApply 129 if plan.UIMode == plans.DestroyMode { 130 // NOTE: This is a vestigial violation of the rule that we mustn't 131 // use plan.UIMode to affect apply-time behavior. It's a design error 132 // if anything downstream switches behavior when operation is set 133 // to walkDestroy, but we've not yet fully audited that. 134 // TODO: Audit that and remove walkDestroy as an operation mode. 135 operation = walkDestroy 136 } 137 138 return graph, operation, diags 139 } 140 141 // ApplyGraphForUI is a last vestage of graphs in the public interface of 142 // Context (as opposed to graphs as an implementation detail) intended only for 143 // use by the "terraform graph" command when asked to render an apply-time 144 // graph. 145 // 146 // The result of this is intended only for rendering ot the user as a dot 147 // graph, and so may change in future in order to make the result more useful 148 // in that context, even if drifts away from the physical graph that Terraform 149 // Core currently uses as an implementation detail of planning. 150 func (c *Context) ApplyGraphForUI(plan *plans.Plan, config *configs.Config) (*Graph, tfdiags.Diagnostics) { 151 // For now though, this really is just the internal graph, confusing 152 // implementation details and all. 153 154 var diags tfdiags.Diagnostics 155 156 graph, _, moreDiags := c.applyGraph(plan, config, false) 157 diags = diags.Append(moreDiags) 158 return graph, diags 159 }