opentofu/terraform/transform_module_variable.go
Mitchell Hashimoto 89f7e3b79f
terraform: add module vars after providers to see references
Fixes #10711

The `ModuleVariablesTransformer` only adds module variables in use. This
was missing module variables used by providers since we ran the provider
too late. This moves the transformer and adds a test for this.
2016-12-13 21:22:21 -08:00

121 lines
3.2 KiB
Go

package terraform
import (
"log"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/dag"
)
// ModuleVariableTransformer is a GraphTransformer that adds all the variables
// in the configuration to the graph.
//
// This only adds variables that are referenced by other things in the graph.
// If a module variable is not referenced, it won't be added to the graph.
type ModuleVariableTransformer struct {
Module *module.Tree
DisablePrune bool // True if pruning unreferenced should be disabled
}
func (t *ModuleVariableTransformer) Transform(g *Graph) error {
return t.transform(g, nil, t.Module)
}
func (t *ModuleVariableTransformer) transform(g *Graph, parent, m *module.Tree) error {
// If no config, no variables
if m == nil {
return nil
}
// Transform all the children. This must be done BEFORE the transform
// above since child module variables can reference parent module variables.
for _, c := range m.Children() {
if err := t.transform(g, m, c); err != nil {
return err
}
}
// If we have a parent, we can determine if a module variable is being
// used, so we transform this.
if parent != nil {
if err := t.transformSingle(g, parent, m); err != nil {
return err
}
}
return nil
}
func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, m *module.Tree) error {
// If we have no vars, we're done!
vars := m.Config().Variables
if len(vars) == 0 {
log.Printf("[TRACE] Module %#v has no variables, skipping.", m.Path())
return nil
}
// Look for usage of this module
var mod *config.Module
for _, modUse := range parent.Config().Modules {
if modUse.Name == m.Name() {
mod = modUse
break
}
}
if mod == nil {
log.Printf("[INFO] Module %#v not used, not adding variables", m.Path())
return nil
}
// Build the reference map so we can determine if we're referencing things.
refMap := NewReferenceMap(g.Vertices())
// Add all variables here
for _, v := range vars {
// Determine the value of the variable. If it isn't in the
// configuration then it was never set and that's not a problem.
var value *config.RawConfig
if raw, ok := mod.RawConfig.Raw[v.Name]; ok {
var err error
value, err = config.NewRawConfig(map[string]interface{}{
v.Name: raw,
})
if err != nil {
// This shouldn't happen because it is already in
// a RawConfig above meaning it worked once before.
panic(err)
}
}
// Build the node.
//
// NOTE: For now this is just an "applyable" variable. As we build
// new graph builders for the other operations I suspect we'll
// find a way to parameterize this, require new transforms, etc.
node := &NodeApplyableModuleVariable{
PathValue: normalizeModulePath(m.Path()),
Config: v,
Value: value,
Module: t.Module,
}
if !t.DisablePrune {
// If the node is not referenced by anything, then we don't need
// to include it since it won't be used.
if matches := refMap.ReferencedBy(node); len(matches) == 0 {
log.Printf(
"[INFO] Not including %q in graph, nothing depends on it",
dag.VertexName(node))
continue
}
}
// Add it!
g.Add(node)
}
return nil
}