github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/states/module.go (about) 1 package states 2 3 import ( 4 "github.com/zclconf/go-cty/cty" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/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, simulataneously 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 ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) 92 93 rs := ms.Resource(addr.Resource) 94 is := rs.EnsureInstance(addr.Key) 95 96 is.Current = obj 97 98 if !is.HasObjects() { 99 // If we have no objects at all then we'll clean up. 100 delete(rs.Instances, addr.Key) 101 } 102 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 103 // Also clean up if we only expect to have one instance anyway 104 // and there are none. We leave the resource behind if an each mode 105 // is active because an empty list or map of instances is a valid state. 106 delete(ms.Resources, addr.Resource.String()) 107 } 108 } 109 110 // SetResourceInstanceDeposed saves the given instance object as a deposed 111 // generation of the resource instance with the given address and deposed key. 112 // 113 // Call this method only for pre-existing deposed objects that already have 114 // a known DeposedKey. For example, this method is useful if reloading objects 115 // that were persisted to a state file. To mark the current object as deposed, 116 // use DeposeResourceInstanceObject instead. 117 // 118 // The resource that contains the given instance must already exist in the 119 // state, or this method will panic. Use Resource to check first if its 120 // presence is not already guaranteed. 121 // 122 // Any existing current instance object for the given resource and deposed key 123 // is overwritten. Set obj to nil to remove the deposed object altogether. If 124 // the instance is left with no objects after this operation then it will 125 // be removed from its containing resource altogether. 126 func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { 127 ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) 128 129 rs := ms.Resource(addr.Resource) 130 is := rs.EnsureInstance(addr.Key) 131 if obj != nil { 132 is.Deposed[key] = obj 133 } else { 134 delete(is.Deposed, key) 135 } 136 137 if !is.HasObjects() { 138 // If we have no objects at all then we'll clean up. 139 delete(rs.Instances, addr.Key) 140 } 141 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 142 // Also clean up if we only expect to have one instance anyway 143 // and there are none. We leave the resource behind if an each mode 144 // is active because an empty list or map of instances is a valid state. 145 delete(ms.Resources, addr.Resource.String()) 146 } 147 } 148 149 // ForgetResourceInstanceDeposed removes the record of the deposed object with 150 // the given address and key, if present. If not present, this is a no-op. 151 func (ms *Module) ForgetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) { 152 rs := ms.Resource(addr.Resource) 153 if rs == nil { 154 return 155 } 156 is := rs.Instance(addr.Key) 157 if is == nil { 158 return 159 } 160 delete(is.Deposed, key) 161 162 if !is.HasObjects() { 163 // If we have no objects at all then we'll clean up. 164 delete(rs.Instances, addr.Key) 165 } 166 if rs.EachMode == NoEach && len(rs.Instances) == 0 { 167 // Also clean up if we only expect to have one instance anyway 168 // and there are none. We leave the resource behind if an each mode 169 // is active because an empty list or map of instances is a valid state. 170 delete(ms.Resources, addr.Resource.String()) 171 } 172 } 173 174 // deposeResourceInstanceObject is the real implementation of 175 // SyncState.DeposeResourceInstanceObject. 176 func (ms *Module) deposeResourceInstanceObject(addr addrs.ResourceInstance, forceKey DeposedKey) DeposedKey { 177 is := ms.ResourceInstance(addr) 178 if is == nil { 179 return NotDeposed 180 } 181 return is.deposeCurrentObject(forceKey) 182 } 183 184 // maybeRestoreResourceInstanceDeposed is the real implementation of 185 // SyncState.MaybeRestoreResourceInstanceDeposed. 186 func (ms *Module) maybeRestoreResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) bool { 187 rs := ms.Resource(addr.Resource) 188 if rs == nil { 189 return false 190 } 191 is := rs.Instance(addr.Key) 192 if is == nil { 193 return false 194 } 195 if is.Current != nil { 196 return false 197 } 198 if len(is.Deposed) == 0 { 199 return false 200 } 201 is.Current = is.Deposed[key] 202 delete(is.Deposed, key) 203 return true 204 } 205 206 // SetOutputValue writes an output value into the state, overwriting any 207 // existing value of the same name. 208 func (ms *Module) SetOutputValue(name string, value cty.Value, sensitive bool) *OutputValue { 209 os := &OutputValue{ 210 Value: value, 211 Sensitive: sensitive, 212 } 213 ms.OutputValues[name] = os 214 return os 215 } 216 217 // RemoveOutputValue removes the output value of the given name from the state, 218 // if it exists. This method is a no-op if there is no value of the given 219 // name. 220 func (ms *Module) RemoveOutputValue(name string) { 221 delete(ms.OutputValues, name) 222 } 223 224 // SetLocalValue writes a local value into the state, overwriting any 225 // existing value of the same name. 226 func (ms *Module) SetLocalValue(name string, value cty.Value) { 227 ms.LocalValues[name] = value 228 } 229 230 // RemoveLocalValue removes the local value of the given name from the state, 231 // if it exists. This method is a no-op if there is no value of the given 232 // name. 233 func (ms *Module) RemoveLocalValue(name string) { 234 delete(ms.LocalValues, name) 235 } 236 237 // PruneResourceHusks is a specialized method that will remove any Resource 238 // objects that do not contain any instances, even if they have an EachMode. 239 // 240 // You probably shouldn't call this! See the method of the same name on 241 // type State for more information on what this is for and the rare situations 242 // where it is safe to use. 243 func (ms *Module) PruneResourceHusks() { 244 for _, rs := range ms.Resources { 245 if len(rs.Instances) == 0 { 246 ms.RemoveResource(rs.Addr) 247 } 248 } 249 } 250 251 // empty returns true if the receving module state is contributing nothing 252 // to the state. In other words, it returns true if the module could be 253 // removed from the state altogether without changing the meaning of the state. 254 // 255 // In practice a module containing no objects is the same as a non-existent 256 // module, and so we can opportunistically clean up once a module becomes 257 // empty on the assumption that it will be re-added if needed later. 258 func (ms *Module) empty() bool { 259 if ms == nil { 260 return true 261 } 262 263 // This must be updated to cover any new collections added to Module 264 // in future. 265 return (len(ms.Resources) == 0 && 266 len(ms.OutputValues) == 0 && 267 len(ms.LocalValues) == 0) 268 }