github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/states/module.go (about) 1 package states 2 3 import ( 4 "github.com/zclconf/go-cty/cty" 5 6 "github.com/hashicorp/terraform/addrs" 7 ) 8 9 // Module is a container for the states of objects within a particular module. 10 type Module struct { 11 Addr addrs.ModuleInstance 12 13 // Resources contains the state for each resource. The keys in this map are 14 // an implementation detail and must not be used by outside callers. 15 Resources map[string]*Resource 16 17 // OutputValues contains the state for each output value. The keys in this 18 // map are output value names. 19 OutputValues map[string]*OutputValue 20 21 // LocalValues contains the value for each named output value. The keys 22 // in this map are local value names. 23 LocalValues map[string]cty.Value 24 } 25 26 // NewModule constructs an empty module state for the given module address. 27 func NewModule(addr addrs.ModuleInstance) *Module { 28 return &Module{ 29 Addr: addr, 30 Resources: map[string]*Resource{}, 31 OutputValues: map[string]*OutputValue{}, 32 LocalValues: map[string]cty.Value{}, 33 } 34 } 35 36 // Resource returns the state for the resource with the given address within 37 // the receiving module state, or nil if the requested resource is not tracked 38 // in the state. 39 func (ms *Module) Resource(addr addrs.Resource) *Resource { 40 return ms.Resources[addr.String()] 41 } 42 43 // ResourceInstance returns the state for the resource instance with the given 44 // address within the receiving module state, or nil if the requested instance 45 // is not tracked in the state. 46 func (ms *Module) ResourceInstance(addr addrs.ResourceInstance) *ResourceInstance { 47 rs := ms.Resource(addr.Resource) 48 if rs == nil { 49 return nil 50 } 51 return rs.Instance(addr.Key) 52 } 53 54 // SetResourceMeta updates the resource-level metadata for the resource 55 // with the given address, creating the resource state for it if it doesn't 56 // already exist. 57 func (ms *Module) SetResourceMeta(addr addrs.Resource, eachMode EachMode, provider addrs.AbsProviderConfig) { 58 rs := ms.Resource(addr) 59 if rs == nil { 60 rs = &Resource{ 61 Addr: addr, 62 Instances: map[addrs.InstanceKey]*ResourceInstance{}, 63 } 64 ms.Resources[addr.String()] = rs 65 } 66 67 rs.EachMode = eachMode 68 rs.ProviderConfig = provider 69 } 70 71 // RemoveResource removes the entire state for the given resource, taking with 72 // it any instances associated with the resource. This should generally be 73 // called only for resource objects whose instances have all been destroyed. 74 func (ms *Module) RemoveResource(addr addrs.Resource) { 75 delete(ms.Resources, addr.String()) 76 } 77 78 // SetResourceInstanceCurrent saves the given instance object as the current 79 // generation of the resource instance with the given address, simultaneously 80 // updating the recorded provider configuration address, dependencies, and 81 // resource EachMode. 82 // 83 // Any existing current instance object for the given resource is overwritten. 84 // Set obj to nil to remove the primary generation object altogether. If there 85 // are no deposed objects then the instance will be removed altogether. 86 // 87 // The provider address and "each mode" are resource-wide settings and so they 88 // are updated for all other instances of the same resource as a side-effect of 89 // this call. 90 func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { 91 rs := ms.Resource(addr.Resource) 92 // if the resource is nil and the object is nil, don't do anything! 93 // you'll probably just cause issues 94 if obj == nil && rs == nil { 95 return 96 } 97 if obj == nil && rs != nil { 98 // does the resource have any other objects? 99 // if not then delete the whole resource 100 // When deleting the resource, ensure that its EachMode is NoEach, 101 // as a resource with EachList or EachMap can have 0 instances and be valid 102 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 103 delete(ms.Resources, addr.Resource.String()) 104 return 105 } 106 // check for an existing resource, now that we've ensured that rs.Instances is more than 0/not nil 107 is := rs.Instance(addr.Key) 108 if is == nil { 109 // if there is no instance on the resource with this address and obj is nil, return and change nothing 110 return 111 } 112 // if we have an instance, update the current 113 is.Current = obj 114 if !is.HasObjects() { 115 // If we have no objects at all then we'll clean up. 116 delete(rs.Instances, addr.Key) 117 // Delete the resource if it has no instances, but only if NoEach 118 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 119 delete(ms.Resources, addr.Resource.String()) 120 return 121 } 122 } 123 // Nothing more to do here, so return! 124 return 125 } 126 if rs == nil && obj != nil { 127 // We don't have have a resource so make one, which is a side effect of setResourceMeta 128 ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) 129 // now we have a resource! so update the rs value to point to it 130 rs = ms.Resource(addr.Resource) 131 } 132 // Get our instance from the resource; it could be there or not at this point 133 is := rs.Instance(addr.Key) 134 if is == nil { 135 // if we don't have a resource, create one and add to the instances 136 is = rs.CreateInstance(addr.Key) 137 // update the resource meta because we have a new instance, so EachMode may have changed 138 ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) 139 } 140 // Update the resource's ProviderConfig, in case the provider has updated 141 rs.ProviderConfig = provider 142 is.Current = obj 143 } 144 145 // SetResourceInstanceDeposed saves the given instance object as a deposed 146 // generation of the resource instance with the given address and deposed key. 147 // 148 // Call this method only for pre-existing deposed objects that already have 149 // a known DeposedKey. For example, this method is useful if reloading objects 150 // that were persisted to a state file. To mark the current object as deposed, 151 // use DeposeResourceInstanceObject instead. 152 // 153 // The resource that contains the given instance must already exist in the 154 // state, or this method will panic. Use Resource to check first if its 155 // presence is not already guaranteed. 156 // 157 // Any existing current instance object for the given resource and deposed key 158 // is overwritten. Set obj to nil to remove the deposed object altogether. If 159 // the instance is left with no objects after this operation then it will 160 // be removed from its containing resource altogether. 161 func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { 162 ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) 163 164 rs := ms.Resource(addr.Resource) 165 is := rs.EnsureInstance(addr.Key) 166 if obj != nil { 167 is.Deposed[key] = obj 168 } else { 169 delete(is.Deposed, key) 170 } 171 172 if !is.HasObjects() { 173 // If we have no objects at all then we'll clean up. 174 delete(rs.Instances, addr.Key) 175 } 176 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 177 // Also clean up if we only expect to have one instance anyway 178 // and there are none. We leave the resource behind if an each mode 179 // is active because an empty list or map of instances is a valid state. 180 delete(ms.Resources, addr.Resource.String()) 181 } 182 } 183 184 // ForgetResourceInstanceAll removes the record of all objects associated with 185 // the specified resource instance, if present. If not present, this is a no-op. 186 func (ms *Module) ForgetResourceInstanceAll(addr addrs.ResourceInstance) { 187 rs := ms.Resource(addr.Resource) 188 if rs == nil { 189 return 190 } 191 delete(rs.Instances, addr.Key) 192 193 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 194 // Also clean up if we only expect to have one instance anyway 195 // and there are none. We leave the resource behind if an each mode 196 // is active because an empty list or map of instances is a valid state. 197 delete(ms.Resources, addr.Resource.String()) 198 } 199 } 200 201 // ForgetResourceInstanceDeposed removes the record of the deposed object with 202 // the given address and key, if present. If not present, this is a no-op. 203 func (ms *Module) ForgetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) { 204 rs := ms.Resource(addr.Resource) 205 if rs == nil { 206 return 207 } 208 is := rs.Instance(addr.Key) 209 if is == nil { 210 return 211 } 212 delete(is.Deposed, key) 213 214 if !is.HasObjects() { 215 // If we have no objects at all then we'll clean up. 216 delete(rs.Instances, addr.Key) 217 } 218 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 219 // Also clean up if we only expect to have one instance anyway 220 // and there are none. We leave the resource behind if an each mode 221 // is active because an empty list or map of instances is a valid state. 222 delete(ms.Resources, addr.Resource.String()) 223 } 224 } 225 226 // deposeResourceInstanceObject is the real implementation of 227 // SyncState.DeposeResourceInstanceObject. 228 func (ms *Module) deposeResourceInstanceObject(addr addrs.ResourceInstance, forceKey DeposedKey) DeposedKey { 229 is := ms.ResourceInstance(addr) 230 if is == nil { 231 return NotDeposed 232 } 233 return is.deposeCurrentObject(forceKey) 234 } 235 236 // maybeRestoreResourceInstanceDeposed is the real implementation of 237 // SyncState.MaybeRestoreResourceInstanceDeposed. 238 func (ms *Module) maybeRestoreResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) bool { 239 rs := ms.Resource(addr.Resource) 240 if rs == nil { 241 return false 242 } 243 is := rs.Instance(addr.Key) 244 if is == nil { 245 return false 246 } 247 if is.Current != nil { 248 return false 249 } 250 if len(is.Deposed) == 0 { 251 return false 252 } 253 is.Current = is.Deposed[key] 254 delete(is.Deposed, key) 255 return true 256 } 257 258 // SetOutputValue writes an output value into the state, overwriting any 259 // existing value of the same name. 260 func (ms *Module) SetOutputValue(name string, value cty.Value, sensitive bool) *OutputValue { 261 os := &OutputValue{ 262 Value: value, 263 Sensitive: sensitive, 264 } 265 ms.OutputValues[name] = os 266 return os 267 } 268 269 // RemoveOutputValue removes the output value of the given name from the state, 270 // if it exists. This method is a no-op if there is no value of the given 271 // name. 272 func (ms *Module) RemoveOutputValue(name string) { 273 delete(ms.OutputValues, name) 274 } 275 276 // SetLocalValue writes a local value into the state, overwriting any 277 // existing value of the same name. 278 func (ms *Module) SetLocalValue(name string, value cty.Value) { 279 ms.LocalValues[name] = value 280 } 281 282 // RemoveLocalValue removes the local value of the given name from the state, 283 // if it exists. This method is a no-op if there is no value of the given 284 // name. 285 func (ms *Module) RemoveLocalValue(name string) { 286 delete(ms.LocalValues, name) 287 } 288 289 // PruneResourceHusks is a specialized method that will remove any Resource 290 // objects that do not contain any instances, even if they have an EachMode. 291 // 292 // You probably shouldn't call this! See the method of the same name on 293 // type State for more information on what this is for and the rare situations 294 // where it is safe to use. 295 func (ms *Module) PruneResourceHusks() { 296 for _, rs := range ms.Resources { 297 if len(rs.Instances) == 0 { 298 ms.RemoveResource(rs.Addr) 299 } 300 } 301 } 302 303 // empty returns true if the receving module state is contributing nothing 304 // to the state. In other words, it returns true if the module could be 305 // removed from the state altogether without changing the meaning of the state. 306 // 307 // In practice a module containing no objects is the same as a non-existent 308 // module, and so we can opportunistically clean up once a module becomes 309 // empty on the assumption that it will be re-added if needed later. 310 func (ms *Module) empty() bool { 311 if ms == nil { 312 return true 313 } 314 315 // This must be updated to cover any new collections added to Module 316 // in future. 317 return (len(ms.Resources) == 0 && 318 len(ms.OutputValues) == 0 && 319 len(ms.LocalValues) == 0) 320 }