github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/plans/changes.go (about) 1 package plans 2 3 import ( 4 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 5 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 6 "github.com/zclconf/go-cty/cty" 7 ) 8 9 // Changes describes various actions that Terraform will attempt to take if 10 // the corresponding plan is applied. 11 // 12 // A Changes object can be rendered into a visual diff (by the caller, using 13 // code in another package) for display to the user. 14 type Changes struct { 15 // Resources tracks planned changes to resource instance objects. 16 Resources []*ResourceInstanceChangeSrc 17 18 // Outputs tracks planned changes output values. 19 // 20 // Note that although an in-memory plan contains planned changes for 21 // outputs throughout the configuration, a plan serialized 22 // to disk retains only the root outputs because they are 23 // externally-visible, while other outputs are implementation details and 24 // can be easily re-calculated during the apply phase. Therefore only root 25 // module outputs will survive a round-trip through a plan file. 26 Outputs []*OutputChangeSrc 27 } 28 29 // NewChanges returns a valid Changes object that describes no changes. 30 func NewChanges() *Changes { 31 return &Changes{} 32 } 33 34 func (c *Changes) Empty() bool { 35 for _, res := range c.Resources { 36 if res.Action != NoOp { 37 return false 38 } 39 } 40 return true 41 } 42 43 // ResourceInstance returns the planned change for the current object of the 44 // resource instance of the given address, if any. Returns nil if no change is 45 // planned. 46 func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc { 47 addrStr := addr.String() 48 for _, rc := range c.Resources { 49 if rc.Addr.String() == addrStr && rc.DeposedKey == states.NotDeposed { 50 return rc 51 } 52 } 53 54 return nil 55 } 56 57 // ResourceInstanceDeposed returns the plan change of a deposed object of 58 // the resource instance of the given address, if any. Returns nil if no change 59 // is planned. 60 func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc { 61 addrStr := addr.String() 62 for _, rc := range c.Resources { 63 if rc.Addr.String() == addrStr && rc.DeposedKey == key { 64 return rc 65 } 66 } 67 68 return nil 69 } 70 71 // OutputValue returns the planned change for the output value with the 72 // given address, if any. Returns nil if no change is planned. 73 func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc { 74 addrStr := addr.String() 75 for _, oc := range c.Outputs { 76 if oc.Addr.String() == addrStr { 77 return oc 78 } 79 } 80 81 return nil 82 } 83 84 // SyncWrapper returns a wrapper object around the receiver that can be used 85 // to make certain changes to the receiver in a concurrency-safe way, as long 86 // as all callers share the same wrapper object. 87 func (c *Changes) SyncWrapper() *ChangesSync { 88 return &ChangesSync{ 89 changes: c, 90 } 91 } 92 93 // ResourceInstanceChange describes a change to a particular resource instance 94 // object. 95 type ResourceInstanceChange struct { 96 // Addr is the absolute address of the resource instance that the change 97 // will apply to. 98 Addr addrs.AbsResourceInstance 99 100 // DeposedKey is the identifier for a deposed object associated with the 101 // given instance, or states.NotDeposed if this change applies to the 102 // current object. 103 // 104 // A Replace change for a resource with create_before_destroy set will 105 // create a new DeposedKey temporarily during replacement. In that case, 106 // DeposedKey in the plan is always states.NotDeposed, representing that 107 // the current object is being replaced with the deposed. 108 DeposedKey states.DeposedKey 109 110 // Provider is the address of the provider configuration that was used 111 // to plan this change, and thus the configuration that must also be 112 // used to apply it. 113 ProviderAddr addrs.AbsProviderConfig 114 115 // Change is an embedded description of the change. 116 Change 117 118 // RequiredReplace is a set of paths that caused the change action to be 119 // Replace rather than Update. Always nil if the change action is not 120 // Replace. 121 // 122 // This is retained only for UI-plan-rendering purposes and so it does not 123 // currently survive a round-trip through a saved plan file. 124 RequiredReplace cty.PathSet 125 126 // Private allows a provider to stash any extra data that is opaque to 127 // Terraform that relates to this change. Terraform will save this 128 // byte-for-byte and return it to the provider in the apply call. 129 Private []byte 130 } 131 132 // Encode produces a variant of the reciever that has its change values 133 // serialized so it can be written to a plan file. Pass the implied type of the 134 // corresponding resource type schema for correct operation. 135 func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) { 136 cs, err := rc.Change.Encode(ty) 137 if err != nil { 138 return nil, err 139 } 140 return &ResourceInstanceChangeSrc{ 141 Addr: rc.Addr, 142 DeposedKey: rc.DeposedKey, 143 ProviderAddr: rc.ProviderAddr, 144 ChangeSrc: *cs, 145 RequiredReplace: rc.RequiredReplace, 146 Private: rc.Private, 147 }, err 148 } 149 150 // Simplify will, where possible, produce a change with a simpler action than 151 // the receiever given a flag indicating whether the caller is dealing with 152 // a normal apply or a destroy. This flag deals with the fact that Terraform 153 // Core uses a specialized graph node type for destroying; only that 154 // specialized node should set "destroying" to true. 155 // 156 // The following table shows the simplification behavior: 157 // 158 // Action Destroying? New Action 159 // --------+-------------+----------- 160 // Create true NoOp 161 // Delete false NoOp 162 // Replace true Delete 163 // Replace false Create 164 // 165 // For any combination not in the above table, the Simplify just returns the 166 // receiver as-is. 167 func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange { 168 if destroying { 169 switch rc.Action { 170 case Delete: 171 // We'll fall out and just return rc verbatim, then. 172 case CreateThenDelete, DeleteThenCreate: 173 return &ResourceInstanceChange{ 174 Addr: rc.Addr, 175 DeposedKey: rc.DeposedKey, 176 Private: rc.Private, 177 ProviderAddr: rc.ProviderAddr, 178 Change: Change{ 179 Action: Delete, 180 Before: rc.Before, 181 After: cty.NullVal(rc.Before.Type()), 182 }, 183 } 184 default: 185 return &ResourceInstanceChange{ 186 Addr: rc.Addr, 187 DeposedKey: rc.DeposedKey, 188 Private: rc.Private, 189 ProviderAddr: rc.ProviderAddr, 190 Change: Change{ 191 Action: NoOp, 192 Before: rc.Before, 193 After: rc.Before, 194 }, 195 } 196 } 197 } else { 198 switch rc.Action { 199 case Delete: 200 return &ResourceInstanceChange{ 201 Addr: rc.Addr, 202 DeposedKey: rc.DeposedKey, 203 Private: rc.Private, 204 ProviderAddr: rc.ProviderAddr, 205 Change: Change{ 206 Action: NoOp, 207 Before: rc.Before, 208 After: rc.Before, 209 }, 210 } 211 case CreateThenDelete, DeleteThenCreate: 212 return &ResourceInstanceChange{ 213 Addr: rc.Addr, 214 DeposedKey: rc.DeposedKey, 215 Private: rc.Private, 216 ProviderAddr: rc.ProviderAddr, 217 Change: Change{ 218 Action: Create, 219 Before: cty.NullVal(rc.After.Type()), 220 After: rc.After, 221 }, 222 } 223 } 224 } 225 226 // If we fall out here then our change is already simple enough. 227 return rc 228 } 229 230 // OutputChange describes a change to an output value. 231 type OutputChange struct { 232 // Addr is the absolute address of the output value that the change 233 // will apply to. 234 Addr addrs.AbsOutputValue 235 236 // Change is an embedded description of the change. 237 // 238 // For output value changes, the type constraint for the DynamicValue 239 // instances is always cty.DynamicPseudoType. 240 Change 241 242 // Sensitive, if true, indicates that either the old or new value in the 243 // change is sensitive and so a rendered version of the plan in the UI 244 // should elide the actual values while still indicating the action of the 245 // change. 246 Sensitive bool 247 } 248 249 // Encode produces a variant of the reciever that has its change values 250 // serialized so it can be written to a plan file. 251 func (oc *OutputChange) Encode() (*OutputChangeSrc, error) { 252 cs, err := oc.Change.Encode(cty.DynamicPseudoType) 253 if err != nil { 254 return nil, err 255 } 256 return &OutputChangeSrc{ 257 Addr: oc.Addr, 258 ChangeSrc: *cs, 259 Sensitive: oc.Sensitive, 260 }, err 261 } 262 263 // Change describes a single change with a given action. 264 type Change struct { 265 // Action defines what kind of change is being made. 266 Action Action 267 268 // Interpretation of Before and After depend on Action: 269 // 270 // NoOp Before and After are the same, unchanged value 271 // Create Before is nil, and After is the expected value after create. 272 // Read Before is any prior value (nil if no prior), and After is the 273 // value that was or will be read. 274 // Update Before is the value prior to update, and After is the expected 275 // value after update. 276 // Replace As with Update. 277 // Delete Before is the value prior to delete, and After is always nil. 278 // 279 // Unknown values may appear anywhere within the Before and After values, 280 // either as the values themselves or as nested elements within known 281 // collections/structures. 282 Before, After cty.Value 283 } 284 285 // Encode produces a variant of the reciever that has its change values 286 // serialized so it can be written to a plan file. Pass the type constraint 287 // that the values are expected to conform to; to properly decode the values 288 // later an identical type constraint must be provided at that time. 289 // 290 // Where a Change is embedded in some other struct, it's generally better 291 // to call the corresponding Encode method of that struct rather than working 292 // directly with its embedded Change. 293 func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) { 294 beforeDV, err := NewDynamicValue(c.Before, ty) 295 if err != nil { 296 return nil, err 297 } 298 afterDV, err := NewDynamicValue(c.After, ty) 299 if err != nil { 300 return nil, err 301 } 302 303 return &ChangeSrc{ 304 Action: c.Action, 305 Before: beforeDV, 306 After: afterDV, 307 }, nil 308 }