instances: Expander.AllInstances
In order to precisely implement the validation rules for "moved" statements we need to be able to test whether particular instances were declared in the configuration. The instance expander is the source of record for which instances we decided while creating a plan, but it's API is far more involved than what our validation rules need, so this new AllInstances method returns a wrapper object with a more straightforward API that provides read-only access to just the question of whether particular instances got registered in the expander already. This API covers all three of the kinds of objects that move statements can refer to. It includes module calls and resources, even though they aren't _themselves_ "instances" in the sense we usually mean, because the module instance addresses they are contained within _are_ instances and so we need to take their dynamic instance keys into account when answering these queries.
This commit is contained in:
parent
57d36c1d9d
commit
51346f0d87
|
@ -197,6 +197,18 @@ func (e *Expander) GetResourceInstanceRepetitionData(addr addrs.AbsResourceInsta
|
|||
return exp.repetitionData(addr.Resource.Key)
|
||||
}
|
||||
|
||||
// AllInstances returns a set of all of the module and resource instances known
|
||||
// to the expander.
|
||||
//
|
||||
// It generally doesn't make sense to call this until everything has already
|
||||
// been fully expanded by calling the SetModule* and SetResource* functions.
|
||||
// After that, the returned set is a convenient small API only for querying
|
||||
// whether particular instance addresses appeared as a result of those
|
||||
// expansions.
|
||||
func (e *Expander) AllInstances() Set {
|
||||
return Set{e}
|
||||
}
|
||||
|
||||
func (e *Expander) findModule(moduleInstAddr addrs.ModuleInstance) *expanderModule {
|
||||
// We expect that all of the modules on the path to our module instance
|
||||
// should already have expansions registered.
|
||||
|
@ -241,6 +253,38 @@ func (e *Expander) setResourceExpansion(parentAddr addrs.ModuleInstance, resourc
|
|||
mod.resources[resourceAddr] = exp
|
||||
}
|
||||
|
||||
func (e *Expander) knowsModuleInstance(want addrs.ModuleInstance) bool {
|
||||
if want.IsRoot() {
|
||||
return true // root module instance is always present
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
return e.exps.knowsModuleInstance(want)
|
||||
}
|
||||
|
||||
func (e *Expander) knowsModuleCall(want addrs.AbsModuleCall) bool {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
return e.exps.knowsModuleCall(want)
|
||||
}
|
||||
|
||||
func (e *Expander) knowsResourceInstance(want addrs.AbsResourceInstance) bool {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
return e.exps.knowsResourceInstance(want)
|
||||
}
|
||||
|
||||
func (e *Expander) knowsResource(want addrs.AbsResource) bool {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
return e.exps.knowsResource(want)
|
||||
}
|
||||
|
||||
type expanderModule struct {
|
||||
moduleCalls map[addrs.ModuleCall]expansion
|
||||
resources map[addrs.Resource]expansion
|
||||
|
@ -360,3 +404,54 @@ func (m *expanderModule) onlyResourceInstances(resourceAddr addrs.Resource, pare
|
|||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *expanderModule) getModuleInstance(want addrs.ModuleInstance) *expanderModule {
|
||||
current := m
|
||||
for _, step := range want {
|
||||
next := current.childInstances[step]
|
||||
if next == nil {
|
||||
return nil
|
||||
}
|
||||
current = next
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func (m *expanderModule) knowsModuleInstance(want addrs.ModuleInstance) bool {
|
||||
return m.getModuleInstance(want) != nil
|
||||
}
|
||||
|
||||
func (m *expanderModule) knowsModuleCall(want addrs.AbsModuleCall) bool {
|
||||
modInst := m.getModuleInstance(want.Module)
|
||||
if modInst == nil {
|
||||
return false
|
||||
}
|
||||
_, ret := modInst.moduleCalls[want.Call]
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *expanderModule) knowsResourceInstance(want addrs.AbsResourceInstance) bool {
|
||||
modInst := m.getModuleInstance(want.Module)
|
||||
if modInst == nil {
|
||||
return false
|
||||
}
|
||||
resourceExp := modInst.resources[want.Resource.Resource]
|
||||
if resourceExp == nil {
|
||||
return false
|
||||
}
|
||||
for _, key := range resourceExp.instanceKeys() {
|
||||
if key == want.Resource.Key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *expanderModule) knowsResource(want addrs.AbsResource) bool {
|
||||
modInst := m.getModuleInstance(want.Module)
|
||||
if modInst == nil {
|
||||
return false
|
||||
}
|
||||
_, ret := modInst.resources[want.Resource]
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package instances
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
)
|
||||
|
||||
// Set is a set of instances, intended mainly for the return value of
|
||||
// Expander.AllInstances, where it therefore represents all of the module
|
||||
// and resource instances known to the expander.
|
||||
type Set struct {
|
||||
// Set currently really just wraps Expander with a reduced API that
|
||||
// only supports lookups, to make it clear that a holder of a Set should
|
||||
// not be modifying the expander any further.
|
||||
exp *Expander
|
||||
}
|
||||
|
||||
// HasModuleInstance returns true if and only if the set contains the module
|
||||
// instance with the given address.
|
||||
func (s Set) HasModuleInstance(want addrs.ModuleInstance) bool {
|
||||
return s.exp.knowsModuleInstance(want)
|
||||
}
|
||||
|
||||
// HasModuleCall returns true if and only if the set contains the module
|
||||
// call with the given address, even if that module call has no instances.
|
||||
func (s Set) HasModuleCall(want addrs.AbsModuleCall) bool {
|
||||
return s.exp.knowsModuleCall(want)
|
||||
}
|
||||
|
||||
// HasResourceInstance returns true if and only if the set contains the resource
|
||||
// instance with the given address.
|
||||
// TODO:
|
||||
func (s Set) HasResourceInstance(want addrs.AbsResourceInstance) bool {
|
||||
return s.exp.knowsResourceInstance(want)
|
||||
}
|
||||
|
||||
// HasResource returns true if and only if the set contains the resource with
|
||||
// the given address, even if that resource has no instances.
|
||||
// TODO:
|
||||
func (s Set) HasResource(want addrs.AbsResource) bool {
|
||||
return s.exp.knowsResource(want)
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
package instances
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
exp := NewExpander()
|
||||
|
||||
// The following constructs the following imaginary module/resource tree:
|
||||
// - root module
|
||||
// - test_thing.single: no repetition
|
||||
// - test_thing.count: count = 1
|
||||
// - test_thing.for_each: for_each = { c = "C" }
|
||||
// - module.single: no repetition
|
||||
// - test_thing.single: no repetition
|
||||
// - module.nested_single: no repetition
|
||||
// - module.zero_count: count = 0
|
||||
// - module.count: count = 2
|
||||
// - module.nested_for_each: [0] for_each = {}, [1] for_each = { e = "E" }
|
||||
// - module.for_each: for_each = { a = "A", b = "B" }
|
||||
// - test_thing.count: ["a"] count = 0, ["b"] count = 1
|
||||
exp.SetModuleSingle(addrs.RootModuleInstance, addrs.ModuleCall{Name: "single"})
|
||||
exp.SetModuleCount(addrs.RootModuleInstance, addrs.ModuleCall{Name: "count"}, 2)
|
||||
exp.SetModuleForEach(addrs.RootModuleInstance, addrs.ModuleCall{Name: "for_each"}, map[string]cty.Value{
|
||||
"a": cty.StringVal("A"),
|
||||
"b": cty.StringVal("B"),
|
||||
})
|
||||
exp.SetModuleSingle(addrs.RootModuleInstance.Child("single", addrs.NoKey), addrs.ModuleCall{Name: "nested_single"})
|
||||
exp.SetModuleForEach(addrs.RootModuleInstance.Child("count", addrs.IntKey(0)), addrs.ModuleCall{Name: "nested_for_each"}, nil)
|
||||
exp.SetModuleForEach(addrs.RootModuleInstance.Child("count", addrs.IntKey(1)), addrs.ModuleCall{Name: "nested_for_each"}, map[string]cty.Value{
|
||||
"e": cty.StringVal("E"),
|
||||
})
|
||||
exp.SetModuleCount(
|
||||
addrs.RootModuleInstance.Child("single", addrs.NoKey).Child("nested_single", addrs.NoKey),
|
||||
addrs.ModuleCall{Name: "zero_count"},
|
||||
0,
|
||||
)
|
||||
|
||||
rAddr := func(name string) addrs.Resource {
|
||||
return addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
exp.SetResourceSingle(addrs.RootModuleInstance, rAddr("single"))
|
||||
exp.SetResourceCount(addrs.RootModuleInstance, rAddr("count"), 1)
|
||||
exp.SetResourceForEach(addrs.RootModuleInstance, rAddr("for_each"), map[string]cty.Value{
|
||||
"c": cty.StringVal("C"),
|
||||
})
|
||||
exp.SetResourceSingle(addrs.RootModuleInstance.Child("single", addrs.NoKey), rAddr("single"))
|
||||
exp.SetResourceCount(addrs.RootModuleInstance.Child("for_each", addrs.StringKey("a")), rAddr("count"), 0)
|
||||
exp.SetResourceCount(addrs.RootModuleInstance.Child("for_each", addrs.StringKey("b")), rAddr("count"), 1)
|
||||
|
||||
set := exp.AllInstances()
|
||||
|
||||
// HasModuleInstance tests
|
||||
if input := addrs.RootModuleInstance; !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey); !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey).Child("nested_single", addrs.NoKey); !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.IntKey(0)); !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.IntKey(1)); !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.IntKey(1)).Child("nested_for_each", addrs.StringKey("e")); !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("for_each", addrs.StringKey("a")); !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("for_each", addrs.StringKey("b")); !set.HasModuleInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.IntKey(0)); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.StringKey("a")); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey).Child("nonexist", addrs.NoKey); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.NoKey); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.IntKey(2)); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.StringKey("a")); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.IntKey(0)).Child("nested_for_each", addrs.StringKey("e")); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey).Child("nested_single", addrs.NoKey).Child("zero_count", addrs.NoKey); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey).Child("nested_single", addrs.NoKey).Child("zero_count", addrs.IntKey(0)); set.HasModuleInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
|
||||
// HasModuleCall tests
|
||||
if input := addrs.RootModuleInstance.ChildCall("single"); !set.HasModuleCall(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey).ChildCall("nested_single"); !set.HasModuleCall(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.ChildCall("count"); !set.HasModuleCall(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.IntKey(0)).ChildCall("nested_for_each"); !set.HasModuleCall(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("count", addrs.IntKey(1)).ChildCall("nested_for_each"); !set.HasModuleCall(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.ChildCall("for_each"); !set.HasModuleCall(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey).Child("nested_single", addrs.NoKey).ChildCall("zero_count"); !set.HasModuleCall(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.ChildCall("nonexist"); set.HasModuleCall(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := addrs.RootModuleInstance.Child("single", addrs.NoKey).ChildCall("nonexist"); set.HasModuleCall(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
|
||||
// HasResourceInstance tests
|
||||
if input := rAddr("single").Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance); !set.HasResourceInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance); !set.HasResourceInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("for_each").Instance(addrs.StringKey("c")).Absolute(addrs.RootModuleInstance); !set.HasResourceInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("single").Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("single", addrs.NoKey)); !set.HasResourceInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance.Child("for_each", addrs.StringKey("b"))); !set.HasResourceInstance(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("single").Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance); set.HasResourceInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("single").Instance(addrs.StringKey("")).Absolute(addrs.RootModuleInstance); set.HasResourceInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance); set.HasResourceInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Instance(addrs.StringKey("")).Absolute(addrs.RootModuleInstance); set.HasResourceInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance); set.HasResourceInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("single").Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("single", addrs.IntKey(0))); set.HasResourceInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance.Child("for_each", addrs.StringKey("a"))); set.HasResourceInstance(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
|
||||
// HasResource tests
|
||||
if input := rAddr("single").Absolute(addrs.RootModuleInstance); !set.HasResource(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Absolute(addrs.RootModuleInstance); !set.HasResource(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("for_each").Absolute(addrs.RootModuleInstance); !set.HasResource(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("single").Absolute(addrs.RootModuleInstance.Child("single", addrs.NoKey)); !set.HasResource(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Absolute(addrs.RootModuleInstance.Child("for_each", addrs.StringKey("a"))); !set.HasResource(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Absolute(addrs.RootModuleInstance.Child("for_each", addrs.StringKey("b"))); !set.HasResource(input) {
|
||||
t.Errorf("missing %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("nonexist").Absolute(addrs.RootModuleInstance); set.HasResource(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
if input := rAddr("count").Absolute(addrs.RootModuleInstance.Child("for_each", addrs.StringKey("nonexist"))); set.HasResource(input) {
|
||||
t.Errorf("unexpected %T %s", input, input.String())
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue