github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/plans/changes.go (about) 1 package plans 2 3 import ( 4 "github.com/iaas-resource-provision/iaas-rpc/internal/addrs" 5 "github.com/iaas-resource-provision/iaas-rpc/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 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 // DeposedKey is the identifier for a deposed object associated with the 151 // given instance, or states.NotDeposed if this change applies to the 152 // current object. 153 // 154 // A Replace change for a resource with create_before_destroy set will 155 // create a new DeposedKey temporarily during replacement. In that case, 156 // DeposedKey in the plan is always states.NotDeposed, representing that 157 // the current object is being replaced with the deposed. 158 DeposedKey states.DeposedKey 159 160 // Provider is the address of the provider configuration that was used 161 // to plan this change, and thus the configuration that must also be 162 // used to apply it. 163 ProviderAddr addrs.AbsProviderConfig 164 165 // Change is an embedded description of the change. 166 Change 167 168 // ActionReason is an optional extra indication of why we chose the 169 // action recorded in Change.Action for this particular resource instance. 170 // 171 // This is an approximate mechanism only for the purpose of explaining the 172 // plan to end-users in the UI and is not to be used for any 173 // decision-making during the apply step; if apply behavior needs to vary 174 // depending on the "action reason" then the information for that decision 175 // must be recorded more precisely elsewhere for that purpose. 176 // 177 // Sometimes there might be more than one reason for choosing a particular 178 // action. In that case, it's up to the codepath making that decision to 179 // decide which value would provide the most relevant explanation to the 180 // end-user and return that. It's not a goal of this field to represent 181 // fine details about the planning process. 182 ActionReason ResourceInstanceChangeActionReason 183 184 // RequiredReplace is a set of paths that caused the change action to be 185 // Replace rather than Update. Always nil if the change action is not 186 // Replace. 187 // 188 // This is retained only for UI-plan-rendering purposes and so it does not 189 // currently survive a round-trip through a saved plan file. 190 RequiredReplace cty.PathSet 191 192 // Private allows a provider to stash any extra data that is opaque to 193 // Terraform that relates to this change. Terraform will save this 194 // byte-for-byte and return it to the provider in the apply call. 195 Private []byte 196 } 197 198 // Encode produces a variant of the reciever that has its change values 199 // serialized so it can be written to a plan file. Pass the implied type of the 200 // corresponding resource type schema for correct operation. 201 func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) { 202 cs, err := rc.Change.Encode(ty) 203 if err != nil { 204 return nil, err 205 } 206 return &ResourceInstanceChangeSrc{ 207 Addr: rc.Addr, 208 DeposedKey: rc.DeposedKey, 209 ProviderAddr: rc.ProviderAddr, 210 ChangeSrc: *cs, 211 ActionReason: rc.ActionReason, 212 RequiredReplace: rc.RequiredReplace, 213 Private: rc.Private, 214 }, err 215 } 216 217 // Simplify will, where possible, produce a change with a simpler action than 218 // the receiever given a flag indicating whether the caller is dealing with 219 // a normal apply or a destroy. This flag deals with the fact that Terraform 220 // Core uses a specialized graph node type for destroying; only that 221 // specialized node should set "destroying" to true. 222 // 223 // The following table shows the simplification behavior: 224 // 225 // Action Destroying? New Action 226 // --------+-------------+----------- 227 // Create true NoOp 228 // Delete false NoOp 229 // Replace true Delete 230 // Replace false Create 231 // 232 // For any combination not in the above table, the Simplify just returns the 233 // receiver as-is. 234 func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange { 235 if destroying { 236 switch rc.Action { 237 case Delete: 238 // We'll fall out and just return rc verbatim, then. 239 case CreateThenDelete, DeleteThenCreate: 240 return &ResourceInstanceChange{ 241 Addr: rc.Addr, 242 DeposedKey: rc.DeposedKey, 243 Private: rc.Private, 244 ProviderAddr: rc.ProviderAddr, 245 Change: Change{ 246 Action: Delete, 247 Before: rc.Before, 248 After: cty.NullVal(rc.Before.Type()), 249 }, 250 } 251 default: 252 return &ResourceInstanceChange{ 253 Addr: rc.Addr, 254 DeposedKey: rc.DeposedKey, 255 Private: rc.Private, 256 ProviderAddr: rc.ProviderAddr, 257 Change: Change{ 258 Action: NoOp, 259 Before: rc.Before, 260 After: rc.Before, 261 }, 262 } 263 } 264 } else { 265 switch rc.Action { 266 case Delete: 267 return &ResourceInstanceChange{ 268 Addr: rc.Addr, 269 DeposedKey: rc.DeposedKey, 270 Private: rc.Private, 271 ProviderAddr: rc.ProviderAddr, 272 Change: Change{ 273 Action: NoOp, 274 Before: rc.Before, 275 After: rc.Before, 276 }, 277 } 278 case CreateThenDelete, DeleteThenCreate: 279 return &ResourceInstanceChange{ 280 Addr: rc.Addr, 281 DeposedKey: rc.DeposedKey, 282 Private: rc.Private, 283 ProviderAddr: rc.ProviderAddr, 284 Change: Change{ 285 Action: Create, 286 Before: cty.NullVal(rc.After.Type()), 287 After: rc.After, 288 }, 289 } 290 } 291 } 292 293 // If we fall out here then our change is already simple enough. 294 return rc 295 } 296 297 // ResourceInstanceChangeActionReason allows for some extra user-facing 298 // reasoning for why a particular change action was chosen for a particular 299 // resource instance. 300 // 301 // This only represents sufficient detail to give a suitable explanation to 302 // an end-user, and mustn't be used for any real decision-making during the 303 // apply step. 304 type ResourceInstanceChangeActionReason rune 305 306 //go:generate go run golang.org/x/tools/cmd/stringer -type=ResourceInstanceChangeActionReason changes.go 307 308 const ( 309 // In most cases there's no special reason for choosing a particular 310 // action, which is represented by ResourceInstanceChangeNoReason. 311 ResourceInstanceChangeNoReason ResourceInstanceChangeActionReason = 0 312 313 // ResourceInstanceReplaceBecauseTainted indicates that the resource 314 // instance must be replaced because its existing current object is 315 // marked as "tainted". 316 ResourceInstanceReplaceBecauseTainted ResourceInstanceChangeActionReason = 'T' 317 318 // ResourceInstanceReplaceByRequest indicates that the resource instance 319 // is planned to be replaced because a caller specifically asked for it 320 // to be using ReplaceAddrs. (On the command line, the -replace=... 321 // planning option.) 322 ResourceInstanceReplaceByRequest ResourceInstanceChangeActionReason = 'R' 323 324 // ResourceInstanceReplaceBecauseCannotUpdate indicates that the resource 325 // instance is planned to be replaced because the provider has indicated 326 // that a requested change cannot be applied as an update. 327 // 328 // In this case, the RequiredReplace field will typically be populated on 329 // the ResourceInstanceChange object to give information about specifically 330 // which arguments changed in a non-updatable way. 331 ResourceInstanceReplaceBecauseCannotUpdate ResourceInstanceChangeActionReason = 'F' 332 ) 333 334 // OutputChange describes a change to an output value. 335 type OutputChange struct { 336 // Addr is the absolute address of the output value that the change 337 // will apply to. 338 Addr addrs.AbsOutputValue 339 340 // Change is an embedded description of the change. 341 // 342 // For output value changes, the type constraint for the DynamicValue 343 // instances is always cty.DynamicPseudoType. 344 Change 345 346 // Sensitive, if true, indicates that either the old or new value in the 347 // change is sensitive and so a rendered version of the plan in the UI 348 // should elide the actual values while still indicating the action of the 349 // change. 350 Sensitive bool 351 } 352 353 // Encode produces a variant of the reciever that has its change values 354 // serialized so it can be written to a plan file. 355 func (oc *OutputChange) Encode() (*OutputChangeSrc, error) { 356 cs, err := oc.Change.Encode(cty.DynamicPseudoType) 357 if err != nil { 358 return nil, err 359 } 360 return &OutputChangeSrc{ 361 Addr: oc.Addr, 362 ChangeSrc: *cs, 363 Sensitive: oc.Sensitive, 364 }, err 365 } 366 367 // Change describes a single change with a given action. 368 type Change struct { 369 // Action defines what kind of change is being made. 370 Action Action 371 372 // Interpretation of Before and After depend on Action: 373 // 374 // NoOp Before and After are the same, unchanged value 375 // Create Before is nil, and After is the expected value after create. 376 // Read Before is any prior value (nil if no prior), and After is the 377 // value that was or will be read. 378 // Update Before is the value prior to update, and After is the expected 379 // value after update. 380 // Replace As with Update. 381 // Delete Before is the value prior to delete, and After is always nil. 382 // 383 // Unknown values may appear anywhere within the Before and After values, 384 // either as the values themselves or as nested elements within known 385 // collections/structures. 386 Before, After cty.Value 387 } 388 389 // Encode produces a variant of the reciever that has its change values 390 // serialized so it can be written to a plan file. Pass the type constraint 391 // that the values are expected to conform to; to properly decode the values 392 // later an identical type constraint must be provided at that time. 393 // 394 // Where a Change is embedded in some other struct, it's generally better 395 // to call the corresponding Encode method of that struct rather than working 396 // directly with its embedded Change. 397 func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) { 398 // Storing unmarked values so that we can encode unmarked values 399 // and save the PathValueMarks for re-marking the values later 400 var beforeVM, afterVM []cty.PathValueMarks 401 unmarkedBefore := c.Before 402 unmarkedAfter := c.After 403 404 if c.Before.ContainsMarked() { 405 unmarkedBefore, beforeVM = c.Before.UnmarkDeepWithPaths() 406 } 407 beforeDV, err := NewDynamicValue(unmarkedBefore, ty) 408 if err != nil { 409 return nil, err 410 } 411 412 if c.After.ContainsMarked() { 413 unmarkedAfter, afterVM = c.After.UnmarkDeepWithPaths() 414 } 415 afterDV, err := NewDynamicValue(unmarkedAfter, ty) 416 if err != nil { 417 return nil, err 418 } 419 420 return &ChangeSrc{ 421 Action: c.Action, 422 Before: beforeDV, 423 After: afterDV, 424 BeforeValMarks: beforeVM, 425 AfterValMarks: afterVM, 426 }, nil 427 }