github.com/pulumi/terraform@v1.4.0/pkg/plans/changes.go (about) 1 package plans 2 3 import ( 4 "github.com/zclconf/go-cty/cty" 5 6 "github.com/pulumi/terraform/pkg/addrs" 7 "github.com/pulumi/terraform/pkg/states" 8 ) 9 10 // Changes describes various actions that Terraform will attempt to take if 11 // the corresponding plan is applied. 12 // 13 // A Changes object can be rendered into a visual diff (by the caller, using 14 // code in another package) for display to the user. 15 type Changes struct { 16 // Resources tracks planned changes to resource instance objects. 17 Resources []*ResourceInstanceChangeSrc 18 19 // Outputs tracks planned changes output values. 20 // 21 // Note that although an in-memory plan contains planned changes for 22 // outputs throughout the configuration, a plan serialized 23 // to disk retains only the root outputs because they are 24 // externally-visible, while other outputs are implementation details and 25 // can be easily re-calculated during the apply phase. Therefore only root 26 // module outputs will survive a round-trip through a plan file. 27 Outputs []*OutputChangeSrc 28 } 29 30 // NewChanges returns a valid Changes object that describes no changes. 31 func NewChanges() *Changes { 32 return &Changes{} 33 } 34 35 func (c *Changes) Empty() bool { 36 for _, res := range c.Resources { 37 if res.Action != NoOp || res.Moved() { 38 return false 39 } 40 } 41 42 for _, out := range c.Outputs { 43 if out.Addr.Module.IsRoot() && out.Action != NoOp { 44 return false 45 } 46 } 47 48 return true 49 } 50 51 // ResourceInstance returns the planned change for the current object of the 52 // resource instance of the given address, if any. Returns nil if no change is 53 // planned. 54 func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc { 55 for _, rc := range c.Resources { 56 if rc.Addr.Equal(addr) && rc.DeposedKey == states.NotDeposed { 57 return rc 58 } 59 } 60 61 return nil 62 63 } 64 65 // InstancesForAbsResource returns the planned change for the current objects 66 // of the resource instances of the given address, if any. Returns nil if no 67 // changes are planned. 68 func (c *Changes) InstancesForAbsResource(addr addrs.AbsResource) []*ResourceInstanceChangeSrc { 69 var changes []*ResourceInstanceChangeSrc 70 for _, rc := range c.Resources { 71 resAddr := rc.Addr.ContainingResource() 72 if resAddr.Equal(addr) && rc.DeposedKey == states.NotDeposed { 73 changes = append(changes, rc) 74 } 75 } 76 77 return changes 78 } 79 80 // InstancesForConfigResource returns the planned change for the current objects 81 // of the resource instances of the given address, if any. Returns nil if no 82 // changes are planned. 83 func (c *Changes) InstancesForConfigResource(addr addrs.ConfigResource) []*ResourceInstanceChangeSrc { 84 var changes []*ResourceInstanceChangeSrc 85 for _, rc := range c.Resources { 86 resAddr := rc.Addr.ContainingResource().Config() 87 if resAddr.Equal(addr) && rc.DeposedKey == states.NotDeposed { 88 changes = append(changes, rc) 89 } 90 } 91 92 return changes 93 } 94 95 // ResourceInstanceDeposed returns the plan change of a deposed object of 96 // the resource instance of the given address, if any. Returns nil if no change 97 // is planned. 98 func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc { 99 for _, rc := range c.Resources { 100 if rc.Addr.Equal(addr) && rc.DeposedKey == key { 101 return rc 102 } 103 } 104 105 return nil 106 } 107 108 // OutputValue returns the planned change for the output value with the 109 // 110 // given address, if any. Returns nil if no change is planned. 111 func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc { 112 for _, oc := range c.Outputs { 113 if oc.Addr.Equal(addr) { 114 return oc 115 } 116 } 117 118 return nil 119 } 120 121 // RootOutputValues returns planned changes for all outputs of the root module. 122 func (c *Changes) RootOutputValues() []*OutputChangeSrc { 123 var res []*OutputChangeSrc 124 125 for _, oc := range c.Outputs { 126 // we can't evaluate root module outputs 127 if !oc.Addr.Module.Equal(addrs.RootModuleInstance) { 128 continue 129 } 130 131 res = append(res, oc) 132 133 } 134 135 return res 136 } 137 138 // OutputValues returns planned changes for all outputs for all module 139 // instances that reside in the parent path. Returns nil if no changes are 140 // planned. 141 func (c *Changes) OutputValues(parent addrs.ModuleInstance, module addrs.ModuleCall) []*OutputChangeSrc { 142 var res []*OutputChangeSrc 143 144 for _, oc := range c.Outputs { 145 // we can't evaluate root module outputs 146 if oc.Addr.Module.Equal(addrs.RootModuleInstance) { 147 continue 148 } 149 150 changeMod, changeCall := oc.Addr.Module.Call() 151 // this does not reside on our parent instance path 152 if !changeMod.Equal(parent) { 153 continue 154 } 155 156 // this is not the module you're looking for 157 if changeCall.Name != module.Name { 158 continue 159 } 160 161 res = append(res, oc) 162 163 } 164 165 return res 166 } 167 168 // SyncWrapper returns a wrapper object around the receiver that can be used 169 // to make certain changes to the receiver in a concurrency-safe way, as long 170 // as all callers share the same wrapper object. 171 func (c *Changes) SyncWrapper() *ChangesSync { 172 return &ChangesSync{ 173 changes: c, 174 } 175 } 176 177 // ResourceInstanceChange describes a change to a particular resource instance 178 // object. 179 type ResourceInstanceChange struct { 180 // Addr is the absolute address of the resource instance that the change 181 // will apply to. 182 Addr addrs.AbsResourceInstance 183 184 // PrevRunAddr is the absolute address that this resource instance had at 185 // the conclusion of a previous run. 186 // 187 // This will typically be the same as Addr, but can be different if the 188 // previous resource instance was subject to a "moved" block that we 189 // handled in the process of creating this plan. 190 // 191 // For the initial creation of a resource instance there isn't really any 192 // meaningful "previous run address", but PrevRunAddr will still be set 193 // equal to Addr in that case in order to simplify logic elsewhere which 194 // aims to detect and react to the movement of instances between addresses. 195 PrevRunAddr addrs.AbsResourceInstance 196 197 // DeposedKey is the identifier for a deposed object associated with the 198 // given instance, or states.NotDeposed if this change applies to the 199 // current object. 200 // 201 // A Replace change for a resource with create_before_destroy set will 202 // create a new DeposedKey temporarily during replacement. In that case, 203 // DeposedKey in the plan is always states.NotDeposed, representing that 204 // the current object is being replaced with the deposed. 205 DeposedKey states.DeposedKey 206 207 // Provider is the address of the provider configuration that was used 208 // to plan this change, and thus the configuration that must also be 209 // used to apply it. 210 ProviderAddr addrs.AbsProviderConfig 211 212 // Change is an embedded description of the change. 213 Change 214 215 // ActionReason is an optional extra indication of why we chose the 216 // action recorded in Change.Action for this particular resource instance. 217 // 218 // This is an approximate mechanism only for the purpose of explaining the 219 // plan to end-users in the UI and is not to be used for any 220 // decision-making during the apply step; if apply behavior needs to vary 221 // depending on the "action reason" then the information for that decision 222 // must be recorded more precisely elsewhere for that purpose. 223 // 224 // Sometimes there might be more than one reason for choosing a particular 225 // action. In that case, it's up to the codepath making that decision to 226 // decide which value would provide the most relevant explanation to the 227 // end-user and return that. It's not a goal of this field to represent 228 // fine details about the planning process. 229 ActionReason ResourceInstanceChangeActionReason 230 231 // RequiredReplace is a set of paths that caused the change action to be 232 // Replace rather than Update. Always nil if the change action is not 233 // Replace. 234 // 235 // This is retained only for UI-plan-rendering purposes and so it does not 236 // currently survive a round-trip through a saved plan file. 237 RequiredReplace cty.PathSet 238 239 // Private allows a provider to stash any extra data that is opaque to 240 // Terraform that relates to this change. Terraform will save this 241 // byte-for-byte and return it to the provider in the apply call. 242 Private []byte 243 } 244 245 // Encode produces a variant of the reciever that has its change values 246 // serialized so it can be written to a plan file. Pass the implied type of the 247 // corresponding resource type schema for correct operation. 248 func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) { 249 cs, err := rc.Change.Encode(ty) 250 if err != nil { 251 return nil, err 252 } 253 prevRunAddr := rc.PrevRunAddr 254 if prevRunAddr.Resource.Resource.Type == "" { 255 // Suggests an old caller that hasn't been properly updated to 256 // populate this yet. 257 prevRunAddr = rc.Addr 258 } 259 return &ResourceInstanceChangeSrc{ 260 Addr: rc.Addr, 261 PrevRunAddr: prevRunAddr, 262 DeposedKey: rc.DeposedKey, 263 ProviderAddr: rc.ProviderAddr, 264 ChangeSrc: *cs, 265 ActionReason: rc.ActionReason, 266 RequiredReplace: rc.RequiredReplace, 267 Private: rc.Private, 268 }, err 269 } 270 271 func (rc *ResourceInstanceChange) Moved() bool { 272 return !rc.Addr.Equal(rc.PrevRunAddr) 273 } 274 275 // Simplify will, where possible, produce a change with a simpler action than 276 // the receiever given a flag indicating whether the caller is dealing with 277 // a normal apply or a destroy. This flag deals with the fact that Terraform 278 // Core uses a specialized graph node type for destroying; only that 279 // specialized node should set "destroying" to true. 280 // 281 // The following table shows the simplification behavior: 282 // 283 // Action Destroying? New Action 284 // --------+-------------+----------- 285 // Create true NoOp 286 // Delete false NoOp 287 // Replace true Delete 288 // Replace false Create 289 // 290 // For any combination not in the above table, the Simplify just returns the 291 // receiver as-is. 292 func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange { 293 if destroying { 294 switch rc.Action { 295 case Delete: 296 // We'll fall out and just return rc verbatim, then. 297 case CreateThenDelete, DeleteThenCreate: 298 return &ResourceInstanceChange{ 299 Addr: rc.Addr, 300 DeposedKey: rc.DeposedKey, 301 Private: rc.Private, 302 ProviderAddr: rc.ProviderAddr, 303 Change: Change{ 304 Action: Delete, 305 Before: rc.Before, 306 After: cty.NullVal(rc.Before.Type()), 307 }, 308 } 309 default: 310 return &ResourceInstanceChange{ 311 Addr: rc.Addr, 312 DeposedKey: rc.DeposedKey, 313 Private: rc.Private, 314 ProviderAddr: rc.ProviderAddr, 315 Change: Change{ 316 Action: NoOp, 317 Before: rc.Before, 318 After: rc.Before, 319 }, 320 } 321 } 322 } else { 323 switch rc.Action { 324 case Delete: 325 return &ResourceInstanceChange{ 326 Addr: rc.Addr, 327 DeposedKey: rc.DeposedKey, 328 Private: rc.Private, 329 ProviderAddr: rc.ProviderAddr, 330 Change: Change{ 331 Action: NoOp, 332 Before: rc.Before, 333 After: rc.Before, 334 }, 335 } 336 case CreateThenDelete, DeleteThenCreate: 337 return &ResourceInstanceChange{ 338 Addr: rc.Addr, 339 DeposedKey: rc.DeposedKey, 340 Private: rc.Private, 341 ProviderAddr: rc.ProviderAddr, 342 Change: Change{ 343 Action: Create, 344 Before: cty.NullVal(rc.After.Type()), 345 After: rc.After, 346 }, 347 } 348 } 349 } 350 351 // If we fall out here then our change is already simple enough. 352 return rc 353 } 354 355 // ResourceInstanceChangeActionReason allows for some extra user-facing 356 // reasoning for why a particular change action was chosen for a particular 357 // resource instance. 358 // 359 // This only represents sufficient detail to give a suitable explanation to 360 // an end-user, and mustn't be used for any real decision-making during the 361 // apply step. 362 type ResourceInstanceChangeActionReason rune 363 364 //go:generate go run golang.org/x/tools/cmd/stringer -type=ResourceInstanceChangeActionReason changes.go 365 366 const ( 367 // In most cases there's no special reason for choosing a particular 368 // action, which is represented by ResourceInstanceChangeNoReason. 369 ResourceInstanceChangeNoReason ResourceInstanceChangeActionReason = 0 370 371 // ResourceInstanceReplaceBecauseTainted indicates that the resource 372 // instance must be replaced because its existing current object is 373 // marked as "tainted". 374 ResourceInstanceReplaceBecauseTainted ResourceInstanceChangeActionReason = 'T' 375 376 // ResourceInstanceReplaceByRequest indicates that the resource instance 377 // is planned to be replaced because a caller specifically asked for it 378 // to be using ReplaceAddrs. (On the command line, the -replace=... 379 // planning option.) 380 ResourceInstanceReplaceByRequest ResourceInstanceChangeActionReason = 'R' 381 382 // ResourceInstanceReplaceByTriggers indicates that the resource instance 383 // is planned to be replaced because of a corresponding change in a 384 // replace_triggered_by reference. 385 ResourceInstanceReplaceByTriggers ResourceInstanceChangeActionReason = 'D' 386 387 // ResourceInstanceReplaceBecauseCannotUpdate indicates that the resource 388 // instance is planned to be replaced because the provider has indicated 389 // that a requested change cannot be applied as an update. 390 // 391 // In this case, the RequiredReplace field will typically be populated on 392 // the ResourceInstanceChange object to give information about specifically 393 // which arguments changed in a non-updatable way. 394 ResourceInstanceReplaceBecauseCannotUpdate ResourceInstanceChangeActionReason = 'F' 395 396 // ResourceInstanceDeleteBecauseNoResourceConfig indicates that the 397 // resource instance is planned to be deleted because there's no 398 // corresponding resource configuration block in the configuration. 399 ResourceInstanceDeleteBecauseNoResourceConfig ResourceInstanceChangeActionReason = 'N' 400 401 // ResourceInstanceDeleteBecauseWrongRepetition indicates that the 402 // resource instance is planned to be deleted because the instance key 403 // type isn't consistent with the repetition mode selected in the 404 // resource configuration. 405 ResourceInstanceDeleteBecauseWrongRepetition ResourceInstanceChangeActionReason = 'W' 406 407 // ResourceInstanceDeleteBecauseCountIndex indicates that the resource 408 // instance is planned to be deleted because its integer instance key 409 // is out of range for the current configured resource "count" value. 410 ResourceInstanceDeleteBecauseCountIndex ResourceInstanceChangeActionReason = 'C' 411 412 // ResourceInstanceDeleteBecauseEachKey indicates that the resource 413 // instance is planned to be deleted because its string instance key 414 // isn't one of the keys included in the current configured resource 415 // "for_each" value. 416 ResourceInstanceDeleteBecauseEachKey ResourceInstanceChangeActionReason = 'E' 417 418 // ResourceInstanceDeleteBecauseNoModule indicates that the resource 419 // instance is planned to be deleted because it belongs to a module 420 // instance that's no longer declared in the configuration. 421 // 422 // This is less specific than the reasons we return for the various ways 423 // a resource instance itself can be no longer declared, including both 424 // the total removal of a module block and changes to its count/for_each 425 // arguments. This difference in detail is out of pragmatism, because 426 // potentially multiple nested modules could all contribute conflicting 427 // specific reasons for a particular instance to no longer be declared. 428 ResourceInstanceDeleteBecauseNoModule ResourceInstanceChangeActionReason = 'M' 429 430 // ResourceInstanceDeleteBecauseNoMoveTarget indicates that the resource 431 // address appears as the target ("to") in a moved block, but no 432 // configuration exists for that resource. According to our move rules, 433 // this combination evaluates to a deletion of the "new" resource. 434 ResourceInstanceDeleteBecauseNoMoveTarget ResourceInstanceChangeActionReason = 'A' 435 436 // ResourceInstanceReadBecauseConfigUnknown indicates that the resource 437 // must be read during apply (rather than during planning) because its 438 // configuration contains unknown values. This reason applies only to 439 // data resources. 440 ResourceInstanceReadBecauseConfigUnknown ResourceInstanceChangeActionReason = '?' 441 442 // ResourceInstanceReadBecauseDependencyPending indicates that the resource 443 // must be read during apply (rather than during planning) because it 444 // depends on a managed resource instance which has its own changes 445 // pending. 446 ResourceInstanceReadBecauseDependencyPending ResourceInstanceChangeActionReason = '!' 447 ) 448 449 // OutputChange describes a change to an output value. 450 type OutputChange struct { 451 // Addr is the absolute address of the output value that the change 452 // will apply to. 453 Addr addrs.AbsOutputValue 454 455 // Change is an embedded description of the change. 456 // 457 // For output value changes, the type constraint for the DynamicValue 458 // instances is always cty.DynamicPseudoType. 459 Change 460 461 // Sensitive, if true, indicates that either the old or new value in the 462 // change is sensitive and so a rendered version of the plan in the UI 463 // should elide the actual values while still indicating the action of the 464 // change. 465 Sensitive bool 466 } 467 468 // Encode produces a variant of the reciever that has its change values 469 // serialized so it can be written to a plan file. 470 func (oc *OutputChange) Encode() (*OutputChangeSrc, error) { 471 cs, err := oc.Change.Encode(cty.DynamicPseudoType) 472 if err != nil { 473 return nil, err 474 } 475 return &OutputChangeSrc{ 476 Addr: oc.Addr, 477 ChangeSrc: *cs, 478 Sensitive: oc.Sensitive, 479 }, err 480 } 481 482 // Change describes a single change with a given action. 483 type Change struct { 484 // Action defines what kind of change is being made. 485 Action Action 486 487 // Interpretation of Before and After depend on Action: 488 // 489 // NoOp Before and After are the same, unchanged value 490 // Create Before is nil, and After is the expected value after create. 491 // Read Before is any prior value (nil if no prior), and After is the 492 // value that was or will be read. 493 // Update Before is the value prior to update, and After is the expected 494 // value after update. 495 // Replace As with Update. 496 // Delete Before is the value prior to delete, and After is always nil. 497 // 498 // Unknown values may appear anywhere within the Before and After values, 499 // either as the values themselves or as nested elements within known 500 // collections/structures. 501 Before, After cty.Value 502 } 503 504 // Encode produces a variant of the reciever that has its change values 505 // serialized so it can be written to a plan file. Pass the type constraint 506 // that the values are expected to conform to; to properly decode the values 507 // later an identical type constraint must be provided at that time. 508 // 509 // Where a Change is embedded in some other struct, it's generally better 510 // to call the corresponding Encode method of that struct rather than working 511 // directly with its embedded Change. 512 func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) { 513 // Storing unmarked values so that we can encode unmarked values 514 // and save the PathValueMarks for re-marking the values later 515 var beforeVM, afterVM []cty.PathValueMarks 516 unmarkedBefore := c.Before 517 unmarkedAfter := c.After 518 519 if c.Before.ContainsMarked() { 520 unmarkedBefore, beforeVM = c.Before.UnmarkDeepWithPaths() 521 } 522 beforeDV, err := NewDynamicValue(unmarkedBefore, ty) 523 if err != nil { 524 return nil, err 525 } 526 527 if c.After.ContainsMarked() { 528 unmarkedAfter, afterVM = c.After.UnmarkDeepWithPaths() 529 } 530 afterDV, err := NewDynamicValue(unmarkedAfter, ty) 531 if err != nil { 532 return nil, err 533 } 534 535 return &ChangeSrc{ 536 Action: c.Action, 537 Before: beforeDV, 538 After: afterDV, 539 BeforeValMarks: beforeVM, 540 AfterValMarks: afterVM, 541 }, nil 542 }