github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/plans/changes.go (about) 1 package plans 2 3 import ( 4 "github.com/hashicorp/terraform/internal/addrs" 5 "github.com/hashicorp/terraform/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 || res.Moved() { 37 return false 38 } 39 } 40 41 for _, out := range c.Outputs { 42 if out.Addr.Module.IsRoot() && out.Action != NoOp { 43 return false 44 } 45 } 46 47 return true 48 } 49 50 // ResourceInstance returns the planned change for the current object of the 51 // resource instance of the given address, if any. Returns nil if no change is 52 // planned. 53 func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc { 54 for _, rc := range c.Resources { 55 if rc.Addr.Equal(addr) && rc.DeposedKey == states.NotDeposed { 56 return rc 57 } 58 } 59 60 return nil 61 62 } 63 64 // InstancesForConfigResource returns the planned change for the current objects 65 // of the resource instances of the given address, if any. Returns nil if no 66 // changes are planned. 67 func (c *Changes) InstancesForConfigResource(addr addrs.ConfigResource) []*ResourceInstanceChangeSrc { 68 var changes []*ResourceInstanceChangeSrc 69 for _, rc := range c.Resources { 70 resAddr := rc.Addr.ContainingResource().Config() 71 if resAddr.Equal(addr) && rc.DeposedKey == states.NotDeposed { 72 changes = append(changes, rc) 73 } 74 } 75 76 return changes 77 } 78 79 // ResourceInstanceDeposed returns the plan change of a deposed object of 80 // the resource instance of the given address, if any. Returns nil if no change 81 // is planned. 82 func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc { 83 for _, rc := range c.Resources { 84 if rc.Addr.Equal(addr) && rc.DeposedKey == key { 85 return rc 86 } 87 } 88 89 return nil 90 } 91 92 // OutputValue returns the planned change for the output value with the 93 // given address, if any. Returns nil if no change is planned. 94 func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc { 95 for _, oc := range c.Outputs { 96 if oc.Addr.Equal(addr) { 97 return oc 98 } 99 } 100 101 return nil 102 } 103 104 // OutputValues returns planned changes for all outputs for all module 105 // instances that reside in the parent path. Returns nil if no changes are 106 // planned. 107 func (c *Changes) OutputValues(parent addrs.ModuleInstance, module addrs.ModuleCall) []*OutputChangeSrc { 108 var res []*OutputChangeSrc 109 110 for _, oc := range c.Outputs { 111 // we can't evaluate root module outputs 112 if oc.Addr.Module.Equal(addrs.RootModuleInstance) { 113 continue 114 } 115 116 changeMod, changeCall := oc.Addr.Module.Call() 117 // this does not reside on our parent instance path 118 if !changeMod.Equal(parent) { 119 continue 120 } 121 122 // this is not the module you're looking for 123 if changeCall.Name != module.Name { 124 continue 125 } 126 127 res = append(res, oc) 128 129 } 130 131 return res 132 } 133 134 // SyncWrapper returns a wrapper object around the receiver that can be used 135 // to make certain changes to the receiver in a concurrency-safe way, as long 136 // as all callers share the same wrapper object. 137 func (c *Changes) SyncWrapper() *ChangesSync { 138 return &ChangesSync{ 139 changes: c, 140 } 141 } 142 143 // ResourceInstanceChange describes a change to a particular resource instance 144 // object. 145 type ResourceInstanceChange struct { 146 // Addr is the absolute address of the resource instance that the change 147 // will apply to. 148 Addr addrs.AbsResourceInstance 149 150 // PrevRunAddr is the absolute address that this resource instance had at 151 // the conclusion of a previous run. 152 // 153 // This will typically be the same as Addr, but can be different if the 154 // previous resource instance was subject to a "moved" block that we 155 // handled in the process of creating this plan. 156 // 157 // For the initial creation of a resource instance there isn't really any 158 // meaningful "previous run address", but PrevRunAddr will still be set 159 // equal to Addr in that case in order to simplify logic elsewhere which 160 // aims to detect and react to the movement of instances between addresses. 161 PrevRunAddr addrs.AbsResourceInstance 162 163 // DeposedKey is the identifier for a deposed object associated with the 164 // given instance, or states.NotDeposed if this change applies to the 165 // current object. 166 // 167 // A Replace change for a resource with create_before_destroy set will 168 // create a new DeposedKey temporarily during replacement. In that case, 169 // DeposedKey in the plan is always states.NotDeposed, representing that 170 // the current object is being replaced with the deposed. 171 DeposedKey states.DeposedKey 172 173 // Provider is the address of the provider configuration that was used 174 // to plan this change, and thus the configuration that must also be 175 // used to apply it. 176 ProviderAddr addrs.AbsProviderConfig 177 178 // Change is an embedded description of the change. 179 Change 180 181 // ActionReason is an optional extra indication of why we chose the 182 // action recorded in Change.Action for this particular resource instance. 183 // 184 // This is an approximate mechanism only for the purpose of explaining the 185 // plan to end-users in the UI and is not to be used for any 186 // decision-making during the apply step; if apply behavior needs to vary 187 // depending on the "action reason" then the information for that decision 188 // must be recorded more precisely elsewhere for that purpose. 189 // 190 // Sometimes there might be more than one reason for choosing a particular 191 // action. In that case, it's up to the codepath making that decision to 192 // decide which value would provide the most relevant explanation to the 193 // end-user and return that. It's not a goal of this field to represent 194 // fine details about the planning process. 195 ActionReason ResourceInstanceChangeActionReason 196 197 // RequiredReplace is a set of paths that caused the change action to be 198 // Replace rather than Update. Always nil if the change action is not 199 // Replace. 200 // 201 // This is retained only for UI-plan-rendering purposes and so it does not 202 // currently survive a round-trip through a saved plan file. 203 RequiredReplace cty.PathSet 204 205 // Private allows a provider to stash any extra data that is opaque to 206 // Terraform that relates to this change. Terraform will save this 207 // byte-for-byte and return it to the provider in the apply call. 208 Private []byte 209 } 210 211 // Encode produces a variant of the reciever that has its change values 212 // serialized so it can be written to a plan file. Pass the implied type of the 213 // corresponding resource type schema for correct operation. 214 func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) { 215 cs, err := rc.Change.Encode(ty) 216 if err != nil { 217 return nil, err 218 } 219 prevRunAddr := rc.PrevRunAddr 220 if prevRunAddr.Resource.Resource.Type == "" { 221 // Suggests an old caller that hasn't been properly updated to 222 // populate this yet. 223 prevRunAddr = rc.Addr 224 } 225 return &ResourceInstanceChangeSrc{ 226 Addr: rc.Addr, 227 PrevRunAddr: prevRunAddr, 228 DeposedKey: rc.DeposedKey, 229 ProviderAddr: rc.ProviderAddr, 230 ChangeSrc: *cs, 231 ActionReason: rc.ActionReason, 232 RequiredReplace: rc.RequiredReplace, 233 Private: rc.Private, 234 }, err 235 } 236 237 // Simplify will, where possible, produce a change with a simpler action than 238 // the receiever given a flag indicating whether the caller is dealing with 239 // a normal apply or a destroy. This flag deals with the fact that Terraform 240 // Core uses a specialized graph node type for destroying; only that 241 // specialized node should set "destroying" to true. 242 // 243 // The following table shows the simplification behavior: 244 // 245 // Action Destroying? New Action 246 // --------+-------------+----------- 247 // Create true NoOp 248 // Delete false NoOp 249 // Replace true Delete 250 // Replace false Create 251 // 252 // For any combination not in the above table, the Simplify just returns the 253 // receiver as-is. 254 func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange { 255 if destroying { 256 switch rc.Action { 257 case Delete: 258 // We'll fall out and just return rc verbatim, then. 259 case CreateThenDelete, DeleteThenCreate: 260 return &ResourceInstanceChange{ 261 Addr: rc.Addr, 262 DeposedKey: rc.DeposedKey, 263 Private: rc.Private, 264 ProviderAddr: rc.ProviderAddr, 265 Change: Change{ 266 Action: Delete, 267 Before: rc.Before, 268 After: cty.NullVal(rc.Before.Type()), 269 }, 270 } 271 default: 272 return &ResourceInstanceChange{ 273 Addr: rc.Addr, 274 DeposedKey: rc.DeposedKey, 275 Private: rc.Private, 276 ProviderAddr: rc.ProviderAddr, 277 Change: Change{ 278 Action: NoOp, 279 Before: rc.Before, 280 After: rc.Before, 281 }, 282 } 283 } 284 } else { 285 switch rc.Action { 286 case Delete: 287 return &ResourceInstanceChange{ 288 Addr: rc.Addr, 289 DeposedKey: rc.DeposedKey, 290 Private: rc.Private, 291 ProviderAddr: rc.ProviderAddr, 292 Change: Change{ 293 Action: NoOp, 294 Before: rc.Before, 295 After: rc.Before, 296 }, 297 } 298 case CreateThenDelete, DeleteThenCreate: 299 return &ResourceInstanceChange{ 300 Addr: rc.Addr, 301 DeposedKey: rc.DeposedKey, 302 Private: rc.Private, 303 ProviderAddr: rc.ProviderAddr, 304 Change: Change{ 305 Action: Create, 306 Before: cty.NullVal(rc.After.Type()), 307 After: rc.After, 308 }, 309 } 310 } 311 } 312 313 // If we fall out here then our change is already simple enough. 314 return rc 315 } 316 317 // ResourceInstanceChangeActionReason allows for some extra user-facing 318 // reasoning for why a particular change action was chosen for a particular 319 // resource instance. 320 // 321 // This only represents sufficient detail to give a suitable explanation to 322 // an end-user, and mustn't be used for any real decision-making during the 323 // apply step. 324 type ResourceInstanceChangeActionReason rune 325 326 //go:generate go run golang.org/x/tools/cmd/stringer -type=ResourceInstanceChangeActionReason changes.go 327 328 const ( 329 // In most cases there's no special reason for choosing a particular 330 // action, which is represented by ResourceInstanceChangeNoReason. 331 ResourceInstanceChangeNoReason ResourceInstanceChangeActionReason = 0 332 333 // ResourceInstanceReplaceBecauseTainted indicates that the resource 334 // instance must be replaced because its existing current object is 335 // marked as "tainted". 336 ResourceInstanceReplaceBecauseTainted ResourceInstanceChangeActionReason = 'T' 337 338 // ResourceInstanceReplaceByRequest indicates that the resource instance 339 // is planned to be replaced because a caller specifically asked for it 340 // to be using ReplaceAddrs. (On the command line, the -replace=... 341 // planning option.) 342 ResourceInstanceReplaceByRequest ResourceInstanceChangeActionReason = 'R' 343 344 // ResourceInstanceReplaceBecauseCannotUpdate indicates that the resource 345 // instance is planned to be replaced because the provider has indicated 346 // that a requested change cannot be applied as an update. 347 // 348 // In this case, the RequiredReplace field will typically be populated on 349 // the ResourceInstanceChange object to give information about specifically 350 // which arguments changed in a non-updatable way. 351 ResourceInstanceReplaceBecauseCannotUpdate ResourceInstanceChangeActionReason = 'F' 352 ) 353 354 // OutputChange describes a change to an output value. 355 type OutputChange struct { 356 // Addr is the absolute address of the output value that the change 357 // will apply to. 358 Addr addrs.AbsOutputValue 359 360 // Change is an embedded description of the change. 361 // 362 // For output value changes, the type constraint for the DynamicValue 363 // instances is always cty.DynamicPseudoType. 364 Change 365 366 // Sensitive, if true, indicates that either the old or new value in the 367 // change is sensitive and so a rendered version of the plan in the UI 368 // should elide the actual values while still indicating the action of the 369 // change. 370 Sensitive bool 371 } 372 373 // Encode produces a variant of the reciever that has its change values 374 // serialized so it can be written to a plan file. 375 func (oc *OutputChange) Encode() (*OutputChangeSrc, error) { 376 cs, err := oc.Change.Encode(cty.DynamicPseudoType) 377 if err != nil { 378 return nil, err 379 } 380 return &OutputChangeSrc{ 381 Addr: oc.Addr, 382 ChangeSrc: *cs, 383 Sensitive: oc.Sensitive, 384 }, err 385 } 386 387 // Change describes a single change with a given action. 388 type Change struct { 389 // Action defines what kind of change is being made. 390 Action Action 391 392 // Interpretation of Before and After depend on Action: 393 // 394 // NoOp Before and After are the same, unchanged value 395 // Create Before is nil, and After is the expected value after create. 396 // Read Before is any prior value (nil if no prior), and After is the 397 // value that was or will be read. 398 // Update Before is the value prior to update, and After is the expected 399 // value after update. 400 // Replace As with Update. 401 // Delete Before is the value prior to delete, and After is always nil. 402 // 403 // Unknown values may appear anywhere within the Before and After values, 404 // either as the values themselves or as nested elements within known 405 // collections/structures. 406 Before, After cty.Value 407 } 408 409 // Encode produces a variant of the reciever that has its change values 410 // serialized so it can be written to a plan file. Pass the type constraint 411 // that the values are expected to conform to; to properly decode the values 412 // later an identical type constraint must be provided at that time. 413 // 414 // Where a Change is embedded in some other struct, it's generally better 415 // to call the corresponding Encode method of that struct rather than working 416 // directly with its embedded Change. 417 func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) { 418 // Storing unmarked values so that we can encode unmarked values 419 // and save the PathValueMarks for re-marking the values later 420 var beforeVM, afterVM []cty.PathValueMarks 421 unmarkedBefore := c.Before 422 unmarkedAfter := c.After 423 424 if c.Before.ContainsMarked() { 425 unmarkedBefore, beforeVM = c.Before.UnmarkDeepWithPaths() 426 } 427 beforeDV, err := NewDynamicValue(unmarkedBefore, ty) 428 if err != nil { 429 return nil, err 430 } 431 432 if c.After.ContainsMarked() { 433 unmarkedAfter, afterVM = c.After.UnmarkDeepWithPaths() 434 } 435 afterDV, err := NewDynamicValue(unmarkedAfter, ty) 436 if err != nil { 437 return nil, err 438 } 439 440 return &ChangeSrc{ 441 Action: c.Action, 442 Before: beforeDV, 443 After: afterDV, 444 BeforeValMarks: beforeVM, 445 AfterValMarks: afterVM, 446 }, nil 447 }