instances: Methods for retrieving the instance keys of modules/resources

These new methods both take into account the possibility of there being
unknown keys, which will be useful for the expression evaluator to decide
whether it can return a specific type or not.
pull/34313/head
Martin Atkins 3 years ago
parent ea0fd23407
commit d9a4f9a06b

@ -138,6 +138,11 @@ const (
NoKeyType InstanceKeyType = 0
IntKeyType InstanceKeyType = 'I'
StringKeyType InstanceKeyType = 'S'
// UnknownKeyType is a placeholder key type for situations where Terraform
// doesn't yet know which key type to use. There are no [InstanceKey]
// values of this type.
UnknownKeyType InstanceKeyType = '?'
)
// toHCLQuotedString is a helper which formats the given string in a way that

@ -314,7 +314,12 @@ func (e *Expander) GetModuleInstanceRepetitionData(addr addrs.ModuleInstance) Re
e.mu.RLock()
defer e.mu.RUnlock()
parentMod := e.findModule(addr[:len(addr)-1])
parentMod, known := e.findModule(addr[:len(addr)-1])
if !known {
// If we're nested inside something unexpanded then we don't even
// know what type of expansion we're doing.
return TotallyUnknownRepetitionData
}
lastStep := addr[len(addr)-1]
exp, ok := parentMod.moduleCalls[addrs.ModuleCall{Name: lastStep.Name}]
if !ok {
@ -323,6 +328,34 @@ func (e *Expander) GetModuleInstanceRepetitionData(addr addrs.ModuleInstance) Re
return exp.repetitionData(lastStep.InstanceKey)
}
// GetModuleCallInstanceKeys determines the child instance keys for one specific
// instance of a module call.
//
// keyType describes the expected type of all keys in knownKeys, which typically
// also implies what data type would be used to describe the full set of
// instances: [addrs.IntKeyType] as a list or tuple, [addrs.StringKeyType] as
// a map or object, and [addrs.NoKeyType] as just a single value.
//
// If unknownKeys is true then there might be additional keys that we can't know
// yet because the call's expansion isn't known.
func (e *Expander) GetModuleCallInstanceKeys(addr addrs.AbsModuleCall) (keyType addrs.InstanceKeyType, knownKeys []addrs.InstanceKey, unknownKeys bool) {
e.mu.RLock()
defer e.mu.RUnlock()
parentMod, known := e.findModule(addr.Module)
if !known {
// If we're nested inside something unexpanded then we don't even
// know yet what kind of instance key to expect. (The caller might
// be able to infer this itself using configuration info, though.)
return addrs.UnknownKeyType, nil, true
}
exp, ok := parentMod.moduleCalls[addr.Call]
if !ok {
panic(fmt.Sprintf("no expansion has been registered for %s", addr))
}
return exp.instanceKeys()
}
// GetResourceInstanceRepetitionData returns an object describing the values
// that should be available for each.key, each.value, and count.index within
// the definition block for the given resource instance.
@ -330,7 +363,12 @@ func (e *Expander) GetResourceInstanceRepetitionData(addr addrs.AbsResourceInsta
e.mu.RLock()
defer e.mu.RUnlock()
parentMod := e.findModule(addr.Module)
parentMod, known := e.findModule(addr.Module)
if !known {
// If we're nested inside something unexpanded then we don't even
// know what type of expansion we're doing.
return TotallyUnknownRepetitionData
}
exp, ok := parentMod.resources[addr.Resource.Resource]
if !ok {
panic(fmt.Sprintf("no expansion has been registered for %s", addr.ContainingResource()))
@ -338,6 +376,34 @@ func (e *Expander) GetResourceInstanceRepetitionData(addr addrs.AbsResourceInsta
return exp.repetitionData(addr.Resource.Key)
}
// GetModuleCallInstanceKeys determines the child instance keys for one specific
// instance of a module call.
//
// keyType describes the expected type of all keys in knownKeys, which typically
// also implies what data type would be used to describe the full set of
// instances: [addrs.IntKeyType] as a list or tuple, [addrs.StringKeyType] as
// a map or object, and [addrs.NoKeyType] as just a single value.
//
// If unknownKeys is true then there might be additional keys that we can't know
// yet because the call's expansion isn't known.
func (e *Expander) ResourceInstanceKeys(addr addrs.AbsResource) (keyType addrs.InstanceKeyType, knownKeys []addrs.InstanceKey, unknownKeys bool) {
e.mu.RLock()
defer e.mu.RUnlock()
parentMod, known := e.findModule(addr.Module)
if !known {
// If we're nested inside something unexpanded then we don't even
// know yet what kind of instance key to expect. (The caller might
// be able to infer this itself using configuration info, though.)
return addrs.UnknownKeyType, nil, true
}
exp, ok := parentMod.resources[addr.Resource]
if !ok {
panic(fmt.Sprintf("no expansion has been registered for %s", addr))
}
return exp.instanceKeys()
}
// AllInstances returns a set of all of the module and resource instances known
// to the expander.
//
@ -350,11 +416,14 @@ func (e *Expander) AllInstances() Set {
return Set{e}
}
func (e *Expander) findModule(moduleInstAddr addrs.ModuleInstance) *expanderModule {
func (e *Expander) findModule(moduleInstAddr addrs.ModuleInstance) (expMod *expanderModule, known bool) {
// We expect that all of the modules on the path to our module instance
// should already have expansions registered.
mod := e.exps
for i, step := range moduleInstAddr {
if expansionIsDeferred(mod.moduleCalls[addrs.ModuleCall{Name: step.Name}]) {
return nil, false
}
next, ok := mod.childInstances[step]
if !ok {
// Top-down ordering of registration is part of the contract of
@ -363,21 +432,25 @@ func (e *Expander) findModule(moduleInstAddr addrs.ModuleInstance) *expanderModu
}
mod = next
}
return mod
return mod, true
}
func (e *Expander) setModuleExpansion(parentAddr addrs.ModuleInstance, callAddr addrs.ModuleCall, exp expansion) {
e.mu.Lock()
defer e.mu.Unlock()
mod := e.findModule(parentAddr)
mod, known := e.findModule(parentAddr)
if !known {
panic(fmt.Sprintf("can't register expansion for call in %s beneath unexpanded parent", parentAddr))
}
if _, exists := mod.moduleCalls[callAddr]; exists {
panic(fmt.Sprintf("expansion already registered for %s", parentAddr.Child(callAddr.Name, addrs.NoKey)))
}
if !expansionIsDeferred(exp) {
// We'll also pre-register the child instances so that later calls can
// populate them as the caller traverses the configuration tree.
for _, key := range exp.instanceKeys() {
_, knownKeys, _ := exp.instanceKeys()
for _, key := range knownKeys {
step := addrs.ModuleInstanceStep{Name: callAddr.Name, InstanceKey: key}
mod.childInstances[step] = newExpanderModule()
}
@ -389,7 +462,10 @@ func (e *Expander) setResourceExpansion(parentAddr addrs.ModuleInstance, resourc
e.mu.Lock()
defer e.mu.Unlock()
mod := e.findModule(parentAddr)
mod, known := e.findModule(parentAddr)
if !known {
panic(fmt.Sprintf("can't register expansion in %s where path includes unknown expansion", parentAddr))
}
if _, exists := mod.resources[resourceAddr]; exists {
panic(fmt.Sprintf("expansion already registered for %s", resourceAddr.Absolute(parentAddr)))
}
@ -481,7 +557,8 @@ func (m *expanderModule) moduleInstances(addr addrs.Module, parentAddr addrs.Mod
// Otherwise, we'll use the expansion from the final step to produce
// a sequence of addresses under this prefix.
for _, k := range exp.instanceKeys() {
_, knownKeys, _ := exp.instanceKeys()
for _, k := range knownKeys {
// We're reusing the buffer under parentAddr as we recurse through
// the structure, so we need to copy it here to produce a final
// immutable slice to return.
@ -665,7 +742,8 @@ func (m *expanderModule) onlyResourceInstances(resourceAddr addrs.Resource, pare
return nil
}
for _, k := range exp.instanceKeys() {
_, knownKeys, _ := exp.instanceKeys()
for _, k := range knownKeys {
// We're reusing the buffer under parentAddr as we recurse through
// the structure, so we need to copy it here to produce a final
// immutable slice to return.
@ -710,7 +788,8 @@ func (m *expanderModule) knowsResourceInstance(want addrs.AbsResourceInstance) b
if resourceExp == nil {
return false
}
for _, key := range resourceExp.instanceKeys() {
_, knownKeys, _ := resourceExp.instanceKeys()
for _, key := range knownKeys {
if key == want.Resource.Key {
return true
}

@ -16,7 +16,7 @@ import (
// ways expansion can operate depending on how repetition is configured for
// an object.
type expansion interface {
instanceKeys() []addrs.InstanceKey
instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool)
repetitionData(addrs.InstanceKey) RepetitionData
}
@ -29,8 +29,8 @@ type expansionSingle uintptr
var singleKeys = []addrs.InstanceKey{addrs.NoKey}
var expansionSingleVal expansionSingle
func (e expansionSingle) instanceKeys() []addrs.InstanceKey {
return singleKeys
func (e expansionSingle) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
return addrs.NoKeyType, singleKeys, false
}
func (e expansionSingle) repetitionData(key addrs.InstanceKey) RepetitionData {
@ -43,12 +43,12 @@ func (e expansionSingle) repetitionData(key addrs.InstanceKey) RepetitionData {
// expansionCount is the expansion corresponding to the "count" argument.
type expansionCount int
func (e expansionCount) instanceKeys() []addrs.InstanceKey {
func (e expansionCount) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
ret := make([]addrs.InstanceKey, int(e))
for i := range ret {
ret[i] = addrs.IntKey(i)
}
return ret
return addrs.IntKeyType, ret, false
}
func (e expansionCount) repetitionData(key addrs.InstanceKey) RepetitionData {
@ -64,7 +64,7 @@ func (e expansionCount) repetitionData(key addrs.InstanceKey) RepetitionData {
// expansionForEach is the expansion corresponding to the "for_each" argument.
type expansionForEach map[string]cty.Value
func (e expansionForEach) instanceKeys() []addrs.InstanceKey {
func (e expansionForEach) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
ret := make([]addrs.InstanceKey, 0, len(e))
for k := range e {
ret = append(ret, addrs.StringKey(k))
@ -72,7 +72,7 @@ func (e expansionForEach) instanceKeys() []addrs.InstanceKey {
sort.Slice(ret, func(i, j int) bool {
return ret[i].(addrs.StringKey) < ret[j].(addrs.StringKey)
})
return ret
return addrs.StringKeyType, ret, false
}
func (e expansionForEach) repetitionData(key addrs.InstanceKey) RepetitionData {
@ -98,12 +98,12 @@ type expansionDeferred rune
const expansionDeferredIntKey = expansionDeferred(addrs.IntKeyType)
const expansionDeferredStringKey = expansionDeferred(addrs.StringKeyType)
func (e expansionDeferred) instanceKeys() []addrs.InstanceKey {
panic("cannot expand when expansion was deferred")
func (e expansionDeferred) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
return addrs.InstanceKeyType(e), nil, true
}
func (e expansionDeferred) repetitionData(key addrs.InstanceKey) RepetitionData {
panic("cannot expand when expansion was deferred")
panic("no known instances for object with deferred expansion")
}
func expansionIsDeferred(exp expansion) bool {

@ -30,6 +30,14 @@ type RepetitionData struct {
EachKey, EachValue cty.Value
}
// TotallyUnknownRepetitionData is a [RepetitionData] value for situations
// where don't even know yet what type of repetition will be used.
var TotallyUnknownRepetitionData = RepetitionData{
CountIndex: cty.UnknownVal(cty.Number),
EachKey: cty.UnknownVal(cty.String),
EachValue: cty.DynamicVal,
}
// UnknownCountRepetitionData is a suitable [RepetitionData] value to use when
// evaluating the configuration of an object which has a count argument that
// is currently unknown.

Loading…
Cancel
Save