add module expansion to validation

We cannot evaluate expansion during validation, since the values may not
be known at that time.

Inject a nodeValidateModule, using the "Concrete" pattern used for other
node types during graph building. This node will always evaluate to a
single module instance, so that we have a valid context within which to
evaluate all sub resources.
This commit is contained in:
James Bardin 2020-04-03 22:30:12 -04:00
parent 5fda76e31d
commit 73492fd2d5
4 changed files with 59 additions and 3 deletions

View File

@ -52,6 +52,7 @@ type PlanGraphBuilder struct {
ConcreteProvider ConcreteProviderNodeFunc ConcreteProvider ConcreteProviderNodeFunc
ConcreteResource ConcreteResourceNodeFunc ConcreteResource ConcreteResourceNodeFunc
ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc
ConcreteModule ConcreteModuleNodeFunc
once sync.Once once sync.Once
} }
@ -140,7 +141,10 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
// Create expansion nodes for all of the module calls. This must // Create expansion nodes for all of the module calls. This must
// come after all other transformers that create nodes representing // come after all other transformers that create nodes representing
// objects that can belong to modules. // objects that can belong to modules.
&ModuleExpansionTransformer{Config: b.Config}, &ModuleExpansionTransformer{
Concrete: b.ConcreteModule,
Config: b.Config,
},
// Connect so that the references are ready for targeting. We'll // Connect so that the references are ready for targeting. We'll
// have to connect again later for providers and so on. // have to connect again later for providers and so on.

View File

@ -27,6 +27,12 @@ func ValidateGraphBuilder(p *PlanGraphBuilder) GraphBuilder {
} }
} }
p.ConcreteModule = func(n *nodeExpandModule) dag.Vertex {
return &nodeValidateModule{
nodeExpandModule: *n,
}
}
// We purposely don't set any other concrete types since they don't // We purposely don't set any other concrete types since they don't
// require validation. // require validation.

View File

@ -5,6 +5,7 @@ import (
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/lang" "github.com/hashicorp/terraform/lang"
) )
@ -14,6 +15,8 @@ type graphNodeModuleCloser interface {
CloseModule() addrs.Module CloseModule() addrs.Module
} }
type ConcreteModuleNodeFunc func(n *nodeExpandModule) dag.Vertex
// nodeExpandModule represents a module call in the configuration that // nodeExpandModule represents a module call in the configuration that
// might expand into multiple module instances depending on how it is // might expand into multiple module instances depending on how it is
// configured. // configured.
@ -214,3 +217,40 @@ func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error)
return nil, nil return nil, nil
} }
// nodeValidateModule wraps a nodeExpand module for validation, ensuring that
// no expansion is attempted during evaluation, when count and for_each
// expressions may not be known.
type nodeValidateModule struct {
nodeExpandModule
}
// GraphNodeEvalable
func (n *nodeValidateModule) EvalTree() EvalNode {
return &evalValidateModule{
Addr: n.Addr,
Config: n.Config,
ModuleCall: n.ModuleCall,
}
}
type evalValidateModule struct {
Addr addrs.Module
Config *configs.Module
ModuleCall *configs.ModuleCall
}
func (n *evalValidateModule) Eval(ctx EvalContext) (interface{}, error) {
_, call := n.Addr.Call()
expander := ctx.InstanceExpander()
// Modules all evaluate to single instances during validation, only to
// create a proper context within which to evaluate. All parent modules
// will be a single instance, but still get our address in the expected
// manner anyway to ensure they've been registered correctly.
for _, module := range expander.ExpandModule(n.Addr.Parent()) {
// now set our own mode to single
ctx.InstanceExpander().SetModuleSingle(module, call)
}
return nil, nil
}

View File

@ -16,7 +16,8 @@ import (
// This transform must be applied only after all nodes representing objects // This transform must be applied only after all nodes representing objects
// that can be contained within modules have already been added. // that can be contained within modules have already been added.
type ModuleExpansionTransformer struct { type ModuleExpansionTransformer struct {
Config *configs.Config Config *configs.Config
Concrete ConcreteModuleNodeFunc
} }
func (t *ModuleExpansionTransformer) Transform(g *Graph) error { func (t *ModuleExpansionTransformer) Transform(g *Graph) error {
@ -36,11 +37,16 @@ func (t *ModuleExpansionTransformer) transform(g *Graph, c *configs.Config, pare
_, call := c.Path.Call() _, call := c.Path.Call()
modCall := c.Parent.Module.ModuleCalls[call.Name] modCall := c.Parent.Module.ModuleCalls[call.Name]
v := &nodeExpandModule{ n := &nodeExpandModule{
Addr: c.Path, Addr: c.Path,
Config: c.Module, Config: c.Module,
ModuleCall: modCall, ModuleCall: modCall,
} }
var v dag.Vertex = n
if t.Concrete != nil {
v = t.Concrete(n)
}
g.Add(v) g.Add(v)
log.Printf("[TRACE] ModuleExpansionTransformer: Added %s as %T", c.Path, v) log.Printf("[TRACE] ModuleExpansionTransformer: Added %s as %T", c.Path, v)