github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/plans/plan.go (about) 1 package plans 2 3 import ( 4 "sort" 5 6 "github.com/hashicorp/terraform/internal/addrs" 7 "github.com/hashicorp/terraform/internal/configs/configschema" 8 "github.com/hashicorp/terraform/internal/lang/globalref" 9 "github.com/hashicorp/terraform/internal/states" 10 "github.com/zclconf/go-cty/cty" 11 ) 12 13 // Plan is the top-level type representing a planned set of changes. 14 // 15 // A plan is a summary of the set of changes required to move from a current 16 // state to a goal state derived from configuration. The described changes 17 // are not applied directly, but contain an approximation of the final 18 // result that will be completed during apply by resolving any values that 19 // cannot be predicted. 20 // 21 // A plan must always be accompanied by the configuration it was built from, 22 // since the plan does not itself include all of the information required to 23 // make the changes indicated. 24 type Plan struct { 25 // Mode is the mode under which this plan was created. 26 // 27 // This is only recorded to allow for UI differences when presenting plans 28 // to the end-user, and so it must not be used to influence apply-time 29 // behavior. The actions during apply must be described entirely by 30 // the Changes field, regardless of how the plan was created. 31 UIMode Mode 32 33 VariableValues map[string]DynamicValue 34 Changes *Changes 35 DriftedResources []*ResourceInstanceChangeSrc 36 TargetAddrs []addrs.Targetable 37 ForceReplaceAddrs []addrs.AbsResourceInstance 38 Backend Backend 39 40 // Checks captures a snapshot of the (probably-incomplete) check results 41 // at the end of the planning process. 42 // 43 // If this plan is applyable (that is, if the planning process completed 44 // without errors) then the set of checks here should be complete even 45 // though some of them will likely have StatusUnknown where the check 46 // condition depends on values we won't know until the apply step. 47 Checks *states.CheckResults 48 49 // RelevantAttributes is a set of resource instance addresses and 50 // attributes that are either directly affected by proposed changes or may 51 // have indirectly contributed to them via references in expressions. 52 // 53 // This is the result of a heuristic and is intended only as a hint to 54 // the UI layer in case it wants to emphasize or de-emphasize certain 55 // resources. Don't use this to drive any non-cosmetic behavior, especially 56 // including anything that would be subject to compatibility constraints. 57 RelevantAttributes []globalref.ResourceAttr 58 59 // PrevRunState and PriorState both describe the situation that the plan 60 // was derived from: 61 // 62 // PrevRunState is a representation of the outcome of the previous 63 // Terraform operation, without any updates from the remote system but 64 // potentially including some changes that resulted from state upgrade 65 // actions. 66 // 67 // PriorState is a representation of the current state of remote objects, 68 // which will differ from PrevRunState if the "refresh" step returned 69 // different data, which might reflect drift. 70 // 71 // PriorState is the main snapshot we use for actions during apply. 72 // PrevRunState is only here so that we can diff PriorState against it in 73 // order to report to the user any out-of-band changes we've detected. 74 PrevRunState *states.State 75 PriorState *states.State 76 } 77 78 // CanApply returns true if and only if the recieving plan includes content 79 // that would make sense to apply. If it returns false, the plan operation 80 // should indicate that there's nothing to do and Terraform should exit 81 // without prompting the user to confirm the changes. 82 // 83 // This function represents our main business logic for making the decision 84 // about whether a given plan represents meaningful "changes", and so its 85 // exact definition may change over time; the intent is just to centralize the 86 // rules for that rather than duplicating different versions of it at various 87 // locations in the UI code. 88 func (p *Plan) CanApply() bool { 89 switch { 90 case !p.Changes.Empty(): 91 // "Empty" means that everything in the changes is a "NoOp", so if 92 // not empty then there's at least one non-NoOp change. 93 return true 94 95 case !p.PriorState.ManagedResourcesEqual(p.PrevRunState): 96 // If there are no changes planned but we detected some 97 // outside-Terraform changes while refreshing then we consider 98 // that applyable in isolation only if this was a refresh-only 99 // plan where we expect updating the state to include these 100 // changes was the intended goal. 101 // 102 // (We don't treat a "refresh only" plan as applyable in normal 103 // planning mode because historically the refresh result wasn't 104 // considered part of a plan at all, and so it would be 105 // a disruptive breaking change if refreshing alone suddenly 106 // became applyable in the normal case and an existing configuration 107 // was relying on ignore_changes in order to be convergent in spite 108 // of intentional out-of-band operations.) 109 return p.UIMode == RefreshOnlyMode 110 111 default: 112 // Otherwise, there are either no changes to apply or they are changes 113 // our cases above don't consider as worthy of applying in isolation. 114 return false 115 } 116 } 117 118 // ProviderAddrs returns a list of all of the provider configuration addresses 119 // referenced throughout the receiving plan. 120 // 121 // The result is de-duplicated so that each distinct address appears only once. 122 func (p *Plan) ProviderAddrs() []addrs.AbsProviderConfig { 123 if p == nil || p.Changes == nil { 124 return nil 125 } 126 127 m := map[string]addrs.AbsProviderConfig{} 128 for _, rc := range p.Changes.Resources { 129 m[rc.ProviderAddr.String()] = rc.ProviderAddr 130 } 131 if len(m) == 0 { 132 return nil 133 } 134 135 // This is mainly just so we'll get stable results for testing purposes. 136 keys := make([]string, 0, len(m)) 137 for k := range m { 138 keys = append(keys, k) 139 } 140 sort.Strings(keys) 141 142 ret := make([]addrs.AbsProviderConfig, len(keys)) 143 for i, key := range keys { 144 ret[i] = m[key] 145 } 146 147 return ret 148 } 149 150 // Backend represents the backend-related configuration and other data as it 151 // existed when a plan was created. 152 type Backend struct { 153 // Type is the type of backend that the plan will apply against. 154 Type string 155 156 // Config is the configuration of the backend, whose schema is decided by 157 // the backend Type. 158 Config DynamicValue 159 160 // Workspace is the name of the workspace that was active when the plan 161 // was created. It is illegal to apply a plan created for one workspace 162 // to the state of another workspace. 163 // (This constraint is already enforced by the statefile lineage mechanism, 164 // but storing this explicitly allows us to return a better error message 165 // in the situation where the user has the wrong workspace selected.) 166 Workspace string 167 } 168 169 func NewBackend(typeName string, config cty.Value, configSchema *configschema.Block, workspaceName string) (*Backend, error) { 170 dv, err := NewDynamicValue(config, configSchema.ImpliedType()) 171 if err != nil { 172 return nil, err 173 } 174 175 return &Backend{ 176 Type: typeName, 177 Config: dv, 178 Workspace: workspaceName, 179 }, nil 180 }