github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plans/changes_src.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package plans 5 6 import ( 7 "fmt" 8 9 "github.com/terramate-io/tf/addrs" 10 "github.com/terramate-io/tf/states" 11 "github.com/zclconf/go-cty/cty" 12 ) 13 14 // ResourceInstanceChangeSrc is a not-yet-decoded ResourceInstanceChange. 15 // Pass the associated resource type's schema type to method Decode to 16 // obtain a ResourceInstanceChange. 17 type ResourceInstanceChangeSrc struct { 18 // Addr is the absolute address of the resource instance that the change 19 // will apply to. 20 Addr addrs.AbsResourceInstance 21 22 // PrevRunAddr is the absolute address that this resource instance had at 23 // the conclusion of a previous run. 24 // 25 // This will typically be the same as Addr, but can be different if the 26 // previous resource instance was subject to a "moved" block that we 27 // handled in the process of creating this plan. 28 // 29 // For the initial creation of a resource instance there isn't really any 30 // meaningful "previous run address", but PrevRunAddr will still be set 31 // equal to Addr in that case in order to simplify logic elsewhere which 32 // aims to detect and react to the movement of instances between addresses. 33 PrevRunAddr addrs.AbsResourceInstance 34 35 // DeposedKey is the identifier for a deposed object associated with the 36 // given instance, or states.NotDeposed if this change applies to the 37 // current object. 38 // 39 // A Replace change for a resource with create_before_destroy set will 40 // create a new DeposedKey temporarily during replacement. In that case, 41 // DeposedKey in the plan is always states.NotDeposed, representing that 42 // the current object is being replaced with the deposed. 43 DeposedKey states.DeposedKey 44 45 // Provider is the address of the provider configuration that was used 46 // to plan this change, and thus the configuration that must also be 47 // used to apply it. 48 ProviderAddr addrs.AbsProviderConfig 49 50 // ChangeSrc is an embedded description of the not-yet-decoded change. 51 ChangeSrc 52 53 // ActionReason is an optional extra indication of why we chose the 54 // action recorded in Change.Action for this particular resource instance. 55 // 56 // This is an approximate mechanism only for the purpose of explaining the 57 // plan to end-users in the UI and is not to be used for any 58 // decision-making during the apply step; if apply behavior needs to vary 59 // depending on the "action reason" then the information for that decision 60 // must be recorded more precisely elsewhere for that purpose. 61 // 62 // See the field of the same name in ResourceInstanceChange for more 63 // details. 64 ActionReason ResourceInstanceChangeActionReason 65 66 // RequiredReplace is a set of paths that caused the change action to be 67 // Replace rather than Update. Always nil if the change action is not 68 // Replace. 69 RequiredReplace cty.PathSet 70 71 // Private allows a provider to stash any extra data that is opaque to 72 // Terraform that relates to this change. Terraform will save this 73 // byte-for-byte and return it to the provider in the apply call. 74 Private []byte 75 } 76 77 // Decode unmarshals the raw representation of the instance object being 78 // changed. Pass the implied type of the corresponding resource type schema 79 // for correct operation. 80 func (rcs *ResourceInstanceChangeSrc) Decode(ty cty.Type) (*ResourceInstanceChange, error) { 81 change, err := rcs.ChangeSrc.Decode(ty) 82 if err != nil { 83 return nil, err 84 } 85 prevRunAddr := rcs.PrevRunAddr 86 if prevRunAddr.Resource.Resource.Type == "" { 87 // Suggests an old caller that hasn't been properly updated to 88 // populate this yet. 89 prevRunAddr = rcs.Addr 90 } 91 return &ResourceInstanceChange{ 92 Addr: rcs.Addr, 93 PrevRunAddr: prevRunAddr, 94 DeposedKey: rcs.DeposedKey, 95 ProviderAddr: rcs.ProviderAddr, 96 Change: *change, 97 ActionReason: rcs.ActionReason, 98 RequiredReplace: rcs.RequiredReplace, 99 Private: rcs.Private, 100 }, nil 101 } 102 103 // DeepCopy creates a copy of the receiver where any pointers to nested mutable 104 // values are also copied, thus ensuring that future mutations of the receiver 105 // will not affect the copy. 106 // 107 // Some types used within a resource change are immutable by convention even 108 // though the Go language allows them to be mutated, such as the types from 109 // the addrs package. These are _not_ copied by this method, under the 110 // assumption that callers will behave themselves. 111 func (rcs *ResourceInstanceChangeSrc) DeepCopy() *ResourceInstanceChangeSrc { 112 if rcs == nil { 113 return nil 114 } 115 ret := *rcs 116 117 ret.RequiredReplace = cty.NewPathSet(ret.RequiredReplace.List()...) 118 119 if len(ret.Private) != 0 { 120 private := make([]byte, len(ret.Private)) 121 copy(private, ret.Private) 122 ret.Private = private 123 } 124 125 ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy() 126 ret.ChangeSrc.After = ret.ChangeSrc.After.Copy() 127 128 return &ret 129 } 130 131 func (rcs *ResourceInstanceChangeSrc) Moved() bool { 132 return !rcs.Addr.Equal(rcs.PrevRunAddr) 133 } 134 135 // OutputChangeSrc describes a change to an output value. 136 type OutputChangeSrc struct { 137 // Addr is the absolute address of the output value that the change 138 // will apply to. 139 Addr addrs.AbsOutputValue 140 141 // ChangeSrc is an embedded description of the not-yet-decoded change. 142 // 143 // For output value changes, the type constraint for the DynamicValue 144 // instances is always cty.DynamicPseudoType. 145 ChangeSrc 146 147 // Sensitive, if true, indicates that either the old or new value in the 148 // change is sensitive and so a rendered version of the plan in the UI 149 // should elide the actual values while still indicating the action of the 150 // change. 151 Sensitive bool 152 } 153 154 // Decode unmarshals the raw representation of the output value being 155 // changed. 156 func (ocs *OutputChangeSrc) Decode() (*OutputChange, error) { 157 change, err := ocs.ChangeSrc.Decode(cty.DynamicPseudoType) 158 if err != nil { 159 return nil, err 160 } 161 return &OutputChange{ 162 Addr: ocs.Addr, 163 Change: *change, 164 Sensitive: ocs.Sensitive, 165 }, nil 166 } 167 168 // DeepCopy creates a copy of the receiver where any pointers to nested mutable 169 // values are also copied, thus ensuring that future mutations of the receiver 170 // will not affect the copy. 171 // 172 // Some types used within a resource change are immutable by convention even 173 // though the Go language allows them to be mutated, such as the types from 174 // the addrs package. These are _not_ copied by this method, under the 175 // assumption that callers will behave themselves. 176 func (ocs *OutputChangeSrc) DeepCopy() *OutputChangeSrc { 177 if ocs == nil { 178 return nil 179 } 180 ret := *ocs 181 182 ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy() 183 ret.ChangeSrc.After = ret.ChangeSrc.After.Copy() 184 185 return &ret 186 } 187 188 // ImportingSrc is the part of a ChangeSrc that describes the embedded import 189 // action. 190 // 191 // The fields in here are subject to change, so downstream consumers should be 192 // prepared for backwards compatibility in case the contents changes. 193 type ImportingSrc struct { 194 // ID is the original ID of the imported resource. 195 ID string 196 } 197 198 // ChangeSrc is a not-yet-decoded Change. 199 type ChangeSrc struct { 200 // Action defines what kind of change is being made. 201 Action Action 202 203 // Before and After correspond to the fields of the same name in Change, 204 // but have not yet been decoded from the serialized value used for 205 // storage. 206 Before, After DynamicValue 207 208 // BeforeValMarks and AfterValMarks are stored path+mark combinations 209 // that might be discovered when encoding a change. Marks are removed 210 // to enable encoding (marked values cannot be marshalled), and so storing 211 // the path+mark combinations allow us to re-mark the value later 212 // when, for example, displaying the diff to the UI. 213 BeforeValMarks, AfterValMarks []cty.PathValueMarks 214 215 // Importing is present if the resource is being imported as part of this 216 // change. 217 // 218 // Use the simple presence of this field to detect if a ChangeSrc is to be 219 // imported, the contents of this structure may be modified going forward. 220 Importing *ImportingSrc 221 222 // GeneratedConfig contains any HCL config generated for this resource 223 // during planning, as a string. If GeneratedConfig is populated, Importing 224 // should be true. However, not all Importing changes contain generated 225 // config. 226 GeneratedConfig string 227 } 228 229 // Decode unmarshals the raw representations of the before and after values 230 // to produce a Change object. Pass the type constraint that the result must 231 // conform to. 232 // 233 // Where a ChangeSrc is embedded in some other struct, it's generally better 234 // to call the corresponding Decode method of that struct rather than working 235 // directly with its embedded Change. 236 func (cs *ChangeSrc) Decode(ty cty.Type) (*Change, error) { 237 var err error 238 before := cty.NullVal(ty) 239 after := cty.NullVal(ty) 240 241 if len(cs.Before) > 0 { 242 before, err = cs.Before.Decode(ty) 243 if err != nil { 244 return nil, fmt.Errorf("error decoding 'before' value: %s", err) 245 } 246 } 247 if len(cs.After) > 0 { 248 after, err = cs.After.Decode(ty) 249 if err != nil { 250 return nil, fmt.Errorf("error decoding 'after' value: %s", err) 251 } 252 } 253 254 var importing *Importing 255 if cs.Importing != nil { 256 importing = &Importing{ID: cs.Importing.ID} 257 } 258 259 return &Change{ 260 Action: cs.Action, 261 Before: before.MarkWithPaths(cs.BeforeValMarks), 262 After: after.MarkWithPaths(cs.AfterValMarks), 263 Importing: importing, 264 GeneratedConfig: cs.GeneratedConfig, 265 }, nil 266 }