github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/instances/expander.go (about) 1 package instances 2 3 import ( 4 "fmt" 5 "sort" 6 "sync" 7 8 "github.com/muratcelep/terraform/not-internal/addrs" 9 "github.com/zclconf/go-cty/cty" 10 ) 11 12 // Expander instances serve as a coordination point for gathering object 13 // repetition values (count and for_each in configuration) and then later 14 // making use of them to fully enumerate all of the instances of an object. 15 // 16 // The two repeatable object types in Terraform are modules and resources. 17 // Because resources belong to modules and modules can nest inside other 18 // modules, module expansion in particular has a recursive effect that can 19 // cause deep objects to expand exponentially. Expander assumes that all 20 // instances of a module have the same static objects inside, and that they 21 // differ only in the repetition count for some of those objects. 22 // 23 // Expander is a synchronized object whose methods can be safely called 24 // from concurrent threads of execution. However, it does expect a certain 25 // sequence of operations which is normally obtained by the caller traversing 26 // a dependency graph: each object must have its repetition mode set exactly 27 // once, and this must be done before any calls that depend on the repetition 28 // mode. In other words, the count or for_each expression value for a module 29 // must be provided before any object nested directly or indirectly inside 30 // that module can be expanded. If this ordering is violated, the methods 31 // will panic to enforce not-internal consistency. 32 // 33 // The Expand* methods of Expander only work directly with modules and with 34 // resources. Addresses for other objects that nest within modules but 35 // do not themselves support repetition can be obtained by calling ExpandModule 36 // with the containing module path and then producing one absolute instance 37 // address per module instance address returned. 38 type Expander struct { 39 mu sync.RWMutex 40 exps *expanderModule 41 } 42 43 // NewExpander initializes and returns a new Expander, empty and ready to use. 44 func NewExpander() *Expander { 45 return &Expander{ 46 exps: newExpanderModule(), 47 } 48 } 49 50 // SetModuleSingle records that the given module call inside the given parent 51 // module does not use any repetition arguments and is therefore a singleton. 52 func (e *Expander) SetModuleSingle(parentAddr addrs.ModuleInstance, callAddr addrs.ModuleCall) { 53 e.setModuleExpansion(parentAddr, callAddr, expansionSingleVal) 54 } 55 56 // SetModuleCount records that the given module call inside the given parent 57 // module instance uses the "count" repetition argument, with the given value. 58 func (e *Expander) SetModuleCount(parentAddr addrs.ModuleInstance, callAddr addrs.ModuleCall, count int) { 59 e.setModuleExpansion(parentAddr, callAddr, expansionCount(count)) 60 } 61 62 // SetModuleForEach records that the given module call inside the given parent 63 // module instance uses the "for_each" repetition argument, with the given 64 // map value. 65 // 66 // In the configuration language the for_each argument can also accept a set. 67 // It's the caller's responsibility to convert that into an identity map before 68 // calling this method. 69 func (e *Expander) SetModuleForEach(parentAddr addrs.ModuleInstance, callAddr addrs.ModuleCall, mapping map[string]cty.Value) { 70 e.setModuleExpansion(parentAddr, callAddr, expansionForEach(mapping)) 71 } 72 73 // SetResourceSingle records that the given resource inside the given module 74 // does not use any repetition arguments and is therefore a singleton. 75 func (e *Expander) SetResourceSingle(moduleAddr addrs.ModuleInstance, resourceAddr addrs.Resource) { 76 e.setResourceExpansion(moduleAddr, resourceAddr, expansionSingleVal) 77 } 78 79 // SetResourceCount records that the given resource inside the given module 80 // uses the "count" repetition argument, with the given value. 81 func (e *Expander) SetResourceCount(moduleAddr addrs.ModuleInstance, resourceAddr addrs.Resource, count int) { 82 e.setResourceExpansion(moduleAddr, resourceAddr, expansionCount(count)) 83 } 84 85 // SetResourceForEach records that the given resource inside the given module 86 // uses the "for_each" repetition argument, with the given map value. 87 // 88 // In the configuration language the for_each argument can also accept a set. 89 // It's the caller's responsibility to convert that into an identity map before 90 // calling this method. 91 func (e *Expander) SetResourceForEach(moduleAddr addrs.ModuleInstance, resourceAddr addrs.Resource, mapping map[string]cty.Value) { 92 e.setResourceExpansion(moduleAddr, resourceAddr, expansionForEach(mapping)) 93 } 94 95 // ExpandModule finds the exhaustive set of module instances resulting from 96 // the expansion of the given module and all of its ancestor modules. 97 // 98 // All of the modules on the path to the identified module must already have 99 // had their expansion registered using one of the SetModule* methods before 100 // calling, or this method will panic. 101 func (e *Expander) ExpandModule(addr addrs.Module) []addrs.ModuleInstance { 102 if len(addr) == 0 { 103 // Root module is always a singleton. 104 return singletonRootModule 105 } 106 107 e.mu.RLock() 108 defer e.mu.RUnlock() 109 110 // We're going to be dynamically growing ModuleInstance addresses, so 111 // we'll preallocate some space to do it so that for typical shallow 112 // module trees we won't need to reallocate this. 113 // (moduleInstances does plenty of allocations itself, so the benefit of 114 // pre-allocating this is marginal but it's not hard to do.) 115 parentAddr := make(addrs.ModuleInstance, 0, 4) 116 ret := e.exps.moduleInstances(addr, parentAddr) 117 sort.SliceStable(ret, func(i, j int) bool { 118 return ret[i].Less(ret[j]) 119 }) 120 return ret 121 } 122 123 // ExpandModuleResource finds the exhaustive set of resource instances resulting from 124 // the expansion of the given resource and all of its containing modules. 125 // 126 // All of the modules on the path to the identified resource and the resource 127 // itself must already have had their expansion registered using one of the 128 // SetModule*/SetResource* methods before calling, or this method will panic. 129 func (e *Expander) ExpandModuleResource(moduleAddr addrs.Module, resourceAddr addrs.Resource) []addrs.AbsResourceInstance { 130 e.mu.RLock() 131 defer e.mu.RUnlock() 132 133 // We're going to be dynamically growing ModuleInstance addresses, so 134 // we'll preallocate some space to do it so that for typical shallow 135 // module trees we won't need to reallocate this. 136 // (moduleInstances does plenty of allocations itself, so the benefit of 137 // pre-allocating this is marginal but it's not hard to do.) 138 moduleInstanceAddr := make(addrs.ModuleInstance, 0, 4) 139 ret := e.exps.moduleResourceInstances(moduleAddr, resourceAddr, moduleInstanceAddr) 140 sort.SliceStable(ret, func(i, j int) bool { 141 return ret[i].Less(ret[j]) 142 }) 143 return ret 144 } 145 146 // ExpandResource finds the set of resource instances resulting from 147 // the expansion of the given resource within its module instance. 148 // 149 // All of the modules on the path to the identified resource and the resource 150 // itself must already have had their expansion registered using one of the 151 // SetModule*/SetResource* methods before calling, or this method will panic. 152 func (e *Expander) ExpandResource(resourceAddr addrs.AbsResource) []addrs.AbsResourceInstance { 153 e.mu.RLock() 154 defer e.mu.RUnlock() 155 156 moduleInstanceAddr := make(addrs.ModuleInstance, 0, 4) 157 ret := e.exps.resourceInstances(resourceAddr.Module, resourceAddr.Resource, moduleInstanceAddr) 158 sort.SliceStable(ret, func(i, j int) bool { 159 return ret[i].Less(ret[j]) 160 }) 161 return ret 162 } 163 164 // GetModuleInstanceRepetitionData returns an object describing the values 165 // that should be available for each.key, each.value, and count.index within 166 // the call block for the given module instance. 167 func (e *Expander) GetModuleInstanceRepetitionData(addr addrs.ModuleInstance) RepetitionData { 168 if len(addr) == 0 { 169 // The root module is always a singleton, so it has no repetition data. 170 return RepetitionData{} 171 } 172 173 e.mu.RLock() 174 defer e.mu.RUnlock() 175 176 parentMod := e.findModule(addr[:len(addr)-1]) 177 lastStep := addr[len(addr)-1] 178 exp, ok := parentMod.moduleCalls[addrs.ModuleCall{Name: lastStep.Name}] 179 if !ok { 180 panic(fmt.Sprintf("no expansion has been registered for %s", addr)) 181 } 182 return exp.repetitionData(lastStep.InstanceKey) 183 } 184 185 // GetResourceInstanceRepetitionData returns an object describing the values 186 // that should be available for each.key, each.value, and count.index within 187 // the definition block for the given resource instance. 188 func (e *Expander) GetResourceInstanceRepetitionData(addr addrs.AbsResourceInstance) RepetitionData { 189 e.mu.RLock() 190 defer e.mu.RUnlock() 191 192 parentMod := e.findModule(addr.Module) 193 exp, ok := parentMod.resources[addr.Resource.Resource] 194 if !ok { 195 panic(fmt.Sprintf("no expansion has been registered for %s", addr.ContainingResource())) 196 } 197 return exp.repetitionData(addr.Resource.Key) 198 } 199 200 // AllInstances returns a set of all of the module and resource instances known 201 // to the expander. 202 // 203 // It generally doesn't make sense to call this until everything has already 204 // been fully expanded by calling the SetModule* and SetResource* functions. 205 // After that, the returned set is a convenient small API only for querying 206 // whether particular instance addresses appeared as a result of those 207 // expansions. 208 func (e *Expander) AllInstances() Set { 209 return Set{e} 210 } 211 212 func (e *Expander) findModule(moduleInstAddr addrs.ModuleInstance) *expanderModule { 213 // We expect that all of the modules on the path to our module instance 214 // should already have expansions registered. 215 mod := e.exps 216 for i, step := range moduleInstAddr { 217 next, ok := mod.childInstances[step] 218 if !ok { 219 // Top-down ordering of registration is part of the contract of 220 // Expander, so this is always indicative of a bug in the caller. 221 panic(fmt.Sprintf("no expansion has been registered for ancestor module %s", moduleInstAddr[:i+1])) 222 } 223 mod = next 224 } 225 return mod 226 } 227 228 func (e *Expander) setModuleExpansion(parentAddr addrs.ModuleInstance, callAddr addrs.ModuleCall, exp expansion) { 229 e.mu.Lock() 230 defer e.mu.Unlock() 231 232 mod := e.findModule(parentAddr) 233 if _, exists := mod.moduleCalls[callAddr]; exists { 234 panic(fmt.Sprintf("expansion already registered for %s", parentAddr.Child(callAddr.Name, addrs.NoKey))) 235 } 236 // We'll also pre-register the child instances so that later calls can 237 // populate them as the caller traverses the configuration tree. 238 for _, key := range exp.instanceKeys() { 239 step := addrs.ModuleInstanceStep{Name: callAddr.Name, InstanceKey: key} 240 mod.childInstances[step] = newExpanderModule() 241 } 242 mod.moduleCalls[callAddr] = exp 243 } 244 245 func (e *Expander) setResourceExpansion(parentAddr addrs.ModuleInstance, resourceAddr addrs.Resource, exp expansion) { 246 e.mu.Lock() 247 defer e.mu.Unlock() 248 249 mod := e.findModule(parentAddr) 250 if _, exists := mod.resources[resourceAddr]; exists { 251 panic(fmt.Sprintf("expansion already registered for %s", resourceAddr.Absolute(parentAddr))) 252 } 253 mod.resources[resourceAddr] = exp 254 } 255 256 func (e *Expander) knowsModuleInstance(want addrs.ModuleInstance) bool { 257 if want.IsRoot() { 258 return true // root module instance is always present 259 } 260 261 e.mu.Lock() 262 defer e.mu.Unlock() 263 264 return e.exps.knowsModuleInstance(want) 265 } 266 267 func (e *Expander) knowsModuleCall(want addrs.AbsModuleCall) bool { 268 e.mu.Lock() 269 defer e.mu.Unlock() 270 271 return e.exps.knowsModuleCall(want) 272 } 273 274 func (e *Expander) knowsResourceInstance(want addrs.AbsResourceInstance) bool { 275 e.mu.Lock() 276 defer e.mu.Unlock() 277 278 return e.exps.knowsResourceInstance(want) 279 } 280 281 func (e *Expander) knowsResource(want addrs.AbsResource) bool { 282 e.mu.Lock() 283 defer e.mu.Unlock() 284 285 return e.exps.knowsResource(want) 286 } 287 288 type expanderModule struct { 289 moduleCalls map[addrs.ModuleCall]expansion 290 resources map[addrs.Resource]expansion 291 childInstances map[addrs.ModuleInstanceStep]*expanderModule 292 } 293 294 func newExpanderModule() *expanderModule { 295 return &expanderModule{ 296 moduleCalls: make(map[addrs.ModuleCall]expansion), 297 resources: make(map[addrs.Resource]expansion), 298 childInstances: make(map[addrs.ModuleInstanceStep]*expanderModule), 299 } 300 } 301 302 var singletonRootModule = []addrs.ModuleInstance{addrs.RootModuleInstance} 303 304 func (m *expanderModule) moduleInstances(addr addrs.Module, parentAddr addrs.ModuleInstance) []addrs.ModuleInstance { 305 callName := addr[0] 306 exp, ok := m.moduleCalls[addrs.ModuleCall{Name: callName}] 307 if !ok { 308 // This is a bug in the caller, because it should always register 309 // expansions for an object and all of its ancestors before requesting 310 // expansion of it. 311 panic(fmt.Sprintf("no expansion has been registered for %s", parentAddr.Child(callName, addrs.NoKey))) 312 } 313 314 var ret []addrs.ModuleInstance 315 316 // If there's more than one step remaining then we need to traverse deeper. 317 if len(addr) > 1 { 318 for step, inst := range m.childInstances { 319 if step.Name != callName { 320 continue 321 } 322 instAddr := append(parentAddr, step) 323 ret = append(ret, inst.moduleInstances(addr[1:], instAddr)...) 324 } 325 return ret 326 } 327 328 // Otherwise, we'll use the expansion from the final step to produce 329 // a sequence of addresses under this prefix. 330 for _, k := range exp.instanceKeys() { 331 // We're reusing the buffer under parentAddr as we recurse through 332 // the structure, so we need to copy it here to produce a final 333 // immutable slice to return. 334 full := make(addrs.ModuleInstance, 0, len(parentAddr)+1) 335 full = append(full, parentAddr...) 336 full = full.Child(callName, k) 337 ret = append(ret, full) 338 } 339 return ret 340 } 341 342 func (m *expanderModule) moduleResourceInstances(moduleAddr addrs.Module, resourceAddr addrs.Resource, parentAddr addrs.ModuleInstance) []addrs.AbsResourceInstance { 343 if len(moduleAddr) > 0 { 344 var ret []addrs.AbsResourceInstance 345 // We need to traverse through the module levels first, so we can 346 // then iterate resource expansions in the context of each module 347 // path leading to them. 348 callName := moduleAddr[0] 349 if _, ok := m.moduleCalls[addrs.ModuleCall{Name: callName}]; !ok { 350 // This is a bug in the caller, because it should always register 351 // expansions for an object and all of its ancestors before requesting 352 // expansion of it. 353 panic(fmt.Sprintf("no expansion has been registered for %s", parentAddr.Child(callName, addrs.NoKey))) 354 } 355 356 for step, inst := range m.childInstances { 357 if step.Name != callName { 358 continue 359 } 360 moduleInstAddr := append(parentAddr, step) 361 ret = append(ret, inst.moduleResourceInstances(moduleAddr[1:], resourceAddr, moduleInstAddr)...) 362 } 363 return ret 364 } 365 366 return m.onlyResourceInstances(resourceAddr, parentAddr) 367 } 368 369 func (m *expanderModule) resourceInstances(moduleAddr addrs.ModuleInstance, resourceAddr addrs.Resource, parentAddr addrs.ModuleInstance) []addrs.AbsResourceInstance { 370 if len(moduleAddr) > 0 { 371 // We need to traverse through the module levels first, using only the 372 // module instances for our specific resource, as the resource may not 373 // yet be expanded in all module instances. 374 step := moduleAddr[0] 375 callName := step.Name 376 if _, ok := m.moduleCalls[addrs.ModuleCall{Name: callName}]; !ok { 377 // This is a bug in the caller, because it should always register 378 // expansions for an object and all of its ancestors before requesting 379 // expansion of it. 380 panic(fmt.Sprintf("no expansion has been registered for %s", parentAddr.Child(callName, addrs.NoKey))) 381 } 382 383 inst := m.childInstances[step] 384 moduleInstAddr := append(parentAddr, step) 385 return inst.resourceInstances(moduleAddr[1:], resourceAddr, moduleInstAddr) 386 } 387 return m.onlyResourceInstances(resourceAddr, parentAddr) 388 } 389 390 func (m *expanderModule) onlyResourceInstances(resourceAddr addrs.Resource, parentAddr addrs.ModuleInstance) []addrs.AbsResourceInstance { 391 var ret []addrs.AbsResourceInstance 392 exp, ok := m.resources[resourceAddr] 393 if !ok { 394 panic(fmt.Sprintf("no expansion has been registered for %s", resourceAddr.Absolute(parentAddr))) 395 } 396 397 for _, k := range exp.instanceKeys() { 398 // We're reusing the buffer under parentAddr as we recurse through 399 // the structure, so we need to copy it here to produce a final 400 // immutable slice to return. 401 moduleAddr := make(addrs.ModuleInstance, len(parentAddr)) 402 copy(moduleAddr, parentAddr) 403 ret = append(ret, resourceAddr.Instance(k).Absolute(moduleAddr)) 404 } 405 return ret 406 } 407 408 func (m *expanderModule) getModuleInstance(want addrs.ModuleInstance) *expanderModule { 409 current := m 410 for _, step := range want { 411 next := current.childInstances[step] 412 if next == nil { 413 return nil 414 } 415 current = next 416 } 417 return current 418 } 419 420 func (m *expanderModule) knowsModuleInstance(want addrs.ModuleInstance) bool { 421 return m.getModuleInstance(want) != nil 422 } 423 424 func (m *expanderModule) knowsModuleCall(want addrs.AbsModuleCall) bool { 425 modInst := m.getModuleInstance(want.Module) 426 if modInst == nil { 427 return false 428 } 429 _, ret := modInst.moduleCalls[want.Call] 430 return ret 431 } 432 433 func (m *expanderModule) knowsResourceInstance(want addrs.AbsResourceInstance) bool { 434 modInst := m.getModuleInstance(want.Module) 435 if modInst == nil { 436 return false 437 } 438 resourceExp := modInst.resources[want.Resource.Resource] 439 if resourceExp == nil { 440 return false 441 } 442 for _, key := range resourceExp.instanceKeys() { 443 if key == want.Resource.Key { 444 return true 445 } 446 } 447 return false 448 } 449 450 func (m *expanderModule) knowsResource(want addrs.AbsResource) bool { 451 modInst := m.getModuleInstance(want.Module) 452 if modInst == nil { 453 return false 454 } 455 _, ret := modInst.resources[want.Resource] 456 return ret 457 }