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  }