github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/plans/changes.go (about) 1 package plans 2 3 import ( 4 "github.com/cycloidio/terraform/addrs" 5 "github.com/cycloidio/terraform/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 // ResourceInstanceDeleteBecauseNoResourceConfig indicates that the 354 // resource instance is planned to be deleted because there's no 355 // corresponding resource configuration block in the configuration. 356 ResourceInstanceDeleteBecauseNoResourceConfig ResourceInstanceChangeActionReason = 'N' 357 358 // ResourceInstanceDeleteBecauseWrongRepetition indicates that the 359 // resource instance is planned to be deleted because the instance key 360 // type isn't consistent with the repetition mode selected in the 361 // resource configuration. 362 ResourceInstanceDeleteBecauseWrongRepetition ResourceInstanceChangeActionReason = 'W' 363 364 // ResourceInstanceDeleteBecauseCountIndex indicates that the resource 365 // instance is planned to be deleted because its integer instance key 366 // is out of range for the current configured resource "count" value. 367 ResourceInstanceDeleteBecauseCountIndex ResourceInstanceChangeActionReason = 'C' 368 369 // ResourceInstanceDeleteBecauseEachKey indicates that the resource 370 // instance is planned to be deleted because its string instance key 371 // isn't one of the keys included in the current configured resource 372 // "for_each" value. 373 ResourceInstanceDeleteBecauseEachKey ResourceInstanceChangeActionReason = 'E' 374 375 // ResourceInstanceDeleteBecauseNoModule indicates that the resource 376 // instance is planned to be deleted because it belongs to a module 377 // instance that's no longer declared in the configuration. 378 // 379 // This is less specific than the reasons we return for the various ways 380 // a resource instance itself can be no longer declared, including both 381 // the total removal of a module block and changes to its count/for_each 382 // arguments. This difference in detail is out of pragmatism, because 383 // potentially multiple nested modules could all contribute conflicting 384 // specific reasons for a particular instance to no longer be declared. 385 ResourceInstanceDeleteBecauseNoModule ResourceInstanceChangeActionReason = 'M' 386 ) 387 388 // OutputChange describes a change to an output value. 389 type OutputChange struct { 390 // Addr is the absolute address of the output value that the change 391 // will apply to. 392 Addr addrs.AbsOutputValue 393 394 // Change is an embedded description of the change. 395 // 396 // For output value changes, the type constraint for the DynamicValue 397 // instances is always cty.DynamicPseudoType. 398 Change 399 400 // Sensitive, if true, indicates that either the old or new value in the 401 // change is sensitive and so a rendered version of the plan in the UI 402 // should elide the actual values while still indicating the action of the 403 // change. 404 Sensitive bool 405 } 406 407 // Encode produces a variant of the reciever that has its change values 408 // serialized so it can be written to a plan file. 409 func (oc *OutputChange) Encode() (*OutputChangeSrc, error) { 410 cs, err := oc.Change.Encode(cty.DynamicPseudoType) 411 if err != nil { 412 return nil, err 413 } 414 return &OutputChangeSrc{ 415 Addr: oc.Addr, 416 ChangeSrc: *cs, 417 Sensitive: oc.Sensitive, 418 }, err 419 } 420 421 // Change describes a single change with a given action. 422 type Change struct { 423 // Action defines what kind of change is being made. 424 Action Action 425 426 // Interpretation of Before and After depend on Action: 427 // 428 // NoOp Before and After are the same, unchanged value 429 // Create Before is nil, and After is the expected value after create. 430 // Read Before is any prior value (nil if no prior), and After is the 431 // value that was or will be read. 432 // Update Before is the value prior to update, and After is the expected 433 // value after update. 434 // Replace As with Update. 435 // Delete Before is the value prior to delete, and After is always nil. 436 // 437 // Unknown values may appear anywhere within the Before and After values, 438 // either as the values themselves or as nested elements within known 439 // collections/structures. 440 Before, After cty.Value 441 } 442 443 // Encode produces a variant of the reciever that has its change values 444 // serialized so it can be written to a plan file. Pass the type constraint 445 // that the values are expected to conform to; to properly decode the values 446 // later an identical type constraint must be provided at that time. 447 // 448 // Where a Change is embedded in some other struct, it's generally better 449 // to call the corresponding Encode method of that struct rather than working 450 // directly with its embedded Change. 451 func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) { 452 // Storing unmarked values so that we can encode unmarked values 453 // and save the PathValueMarks for re-marking the values later 454 var beforeVM, afterVM []cty.PathValueMarks 455 unmarkedBefore := c.Before 456 unmarkedAfter := c.After 457 458 if c.Before.ContainsMarked() { 459 unmarkedBefore, beforeVM = c.Before.UnmarkDeepWithPaths() 460 } 461 beforeDV, err := NewDynamicValue(unmarkedBefore, ty) 462 if err != nil { 463 return nil, err 464 } 465 466 if c.After.ContainsMarked() { 467 unmarkedAfter, afterVM = c.After.UnmarkDeepWithPaths() 468 } 469 afterDV, err := NewDynamicValue(unmarkedAfter, ty) 470 if err != nil { 471 return nil, err 472 } 473 474 return &ChangeSrc{ 475 Action: c.Action, 476 Before: beforeDV, 477 After: afterDV, 478 BeforeValMarks: beforeVM, 479 AfterValMarks: afterVM, 480 }, nil 481 }