Add ModuleOutputs method to states

In order to efficiently build the module objects for evaluation, we need
to collect the outputs from a set of module instances. The ModuleOutputs
method will return a copy of the state outputs, while not requiring the
unnecessary copying of each entire module.
This commit is contained in:
James Bardin 2020-04-13 17:59:09 -04:00
parent e9eb8e04cc
commit 2c4c027a97
3 changed files with 98 additions and 0 deletions

View File

@ -82,6 +82,35 @@ func (s *State) ModuleInstances(addr addrs.Module) []*Module {
return ms
}
// ModuleOutputs returns all outputs for the given module call under the
// parentAddr instance.
func (s *State) ModuleOutputs(parentAddr addrs.ModuleInstance, module addrs.ModuleCall) []*OutputValue {
var os []*OutputValue
for _, m := range s.Modules {
// can't get outputs from the root module
if m.Addr.IsRoot() {
continue
}
parent, call := m.Addr.Call()
// make sure this is a descendent in the correct path
if !parentAddr.Equal(parent) {
continue
}
// and check if this is the correct child
if call.Name != module.Name {
continue
}
for _, o := range m.OutputValues {
os = append(os, o)
}
}
return os
}
// RemoveModule removes the module with the given address from the state,
// unless it is the root module. The root module cannot be deleted, and so
// this method will panic if that is attempted.

View File

@ -43,6 +43,10 @@ func TestState(t *testing.T) {
childModule := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
childModule.SetOutputValue("pizza", cty.StringVal("hawaiian"), false)
multiModA := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")))
multiModA.SetOutputValue("pizza", cty.StringVal("cheese"), false)
multiModB := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")))
multiModB.SetOutputValue("pizza", cty.StringVal("sausage"), false)
want := &State{
Modules: map[string]*Module{
@ -114,6 +118,40 @@ func TestState(t *testing.T) {
},
Resources: map[string]*Resource{},
},
`module.multi["a"]`: {
Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")),
LocalValues: map[string]cty.Value{},
OutputValues: map[string]*OutputValue{
"pizza": {
Addr: addrs.AbsOutputValue{
Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")),
OutputValue: addrs.OutputValue{
Name: "pizza",
},
},
Value: cty.StringVal("cheese"),
Sensitive: false,
},
},
Resources: map[string]*Resource{},
},
`module.multi["b"]`: {
Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")),
LocalValues: map[string]cty.Value{},
OutputValues: map[string]*OutputValue{
"pizza": {
Addr: addrs.AbsOutputValue{
Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")),
OutputValue: addrs.OutputValue{
Name: "pizza",
},
},
Value: cty.StringVal("sausage"),
Sensitive: false,
},
},
Resources: map[string]*Resource{},
},
},
}
@ -133,6 +171,25 @@ func TestState(t *testing.T) {
for _, problem := range deep.Equal(state, want) {
t.Error(problem)
}
expectedOutputs := map[string]string{
`module.multi["a"].output.pizza`: "cheese",
`module.multi["b"].output.pizza`: "sausage",
}
for _, o := range state.ModuleOutputs(addrs.RootModuleInstance, addrs.ModuleCall{Name: "multi"}) {
addr := o.Addr.String()
expected := expectedOutputs[addr]
delete(expectedOutputs, addr)
if expected != o.Value.AsString() {
t.Fatalf("expected %q:%q, got %q", addr, expected, o.Value.AsString())
}
}
for addr, o := range expectedOutputs {
t.Fatalf("missing output %q:%q", addr, o)
}
}
func TestStateDeepCopy(t *testing.T) {

View File

@ -48,6 +48,18 @@ func (s *SyncState) Module(addr addrs.ModuleInstance) *Module {
return ret
}
// ModuleOutputs returns the set of OutputValues that matches the given path.
func (s *SyncState) ModuleOutputs(parentAddr addrs.ModuleInstance, module addrs.ModuleCall) []*OutputValue {
s.lock.RLock()
defer s.lock.RUnlock()
var os []*OutputValue
for _, o := range s.state.ModuleOutputs(parentAddr, module) {
os = append(os, o.DeepCopy())
}
return os
}
// RemoveModule removes the entire state for the given module, taking with
// it any resources associated with the module. This should generally be
// called only for modules whose resources have all been destroyed, but