terraform: first stap at module variables, going to redo some things
This commit is contained in:
parent
6376c4ca9b
commit
4dfdc52ba0
|
@ -63,6 +63,9 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
|||
&MissingProvisionerTransformer{Provisioners: b.Provisioners},
|
||||
&ProvisionerTransformer{},
|
||||
|
||||
// Add module variables
|
||||
&VariableTransformer{Module: b.Module},
|
||||
|
||||
// Add the outputs
|
||||
&OutputTransformer{Module: b.Module},
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
)
|
||||
|
||||
// NodeApplyableVariable represents a variable during the apply step.
|
||||
type NodeApplyableVariable struct {
|
||||
PathValue []string
|
||||
Config *config.Variable // Config is the var in the config
|
||||
Value *config.RawConfig // Value is the value that is set
|
||||
|
||||
Module *module.Tree // Antiquated, want to remove
|
||||
}
|
||||
|
||||
func (n *NodeApplyableVariable) Name() string {
|
||||
result := fmt.Sprintf("var.%s", n.Config.Name)
|
||||
if len(n.PathValue) > 1 {
|
||||
result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GraphNodeSubPath
|
||||
func (n *NodeApplyableVariable) Path() []string {
|
||||
// We execute in the parent scope (above our own module) so that
|
||||
// we can access the proper interpolations.
|
||||
if len(n.PathValue) > 2 {
|
||||
return n.PathValue[:len(n.PathValue)-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GraphNodeReferenceGlobal
|
||||
func (n *NodeApplyableVariable) ReferenceGlobal() bool {
|
||||
// We have to create fully qualified references because we cross
|
||||
// boundaries here: our ReferenceableName is in one path and our
|
||||
// References are from another path.
|
||||
return true
|
||||
}
|
||||
|
||||
// GraphNodeReferenceable
|
||||
func (n *NodeApplyableVariable) ReferenceableName() []string {
|
||||
return []string{n.Name()}
|
||||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeApplyableVariable) EvalTree() EvalNode {
|
||||
// If we have no value, do nothing
|
||||
if n.Value == nil {
|
||||
return &EvalNoop{}
|
||||
}
|
||||
|
||||
// Otherwise, interpolate the value of this variable and set it
|
||||
// within the variables mapping.
|
||||
var config *ResourceConfig
|
||||
variables := make(map[string]interface{})
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalInterpolate{
|
||||
Config: n.Value,
|
||||
Output: &config,
|
||||
},
|
||||
|
||||
&EvalVariableBlock{
|
||||
Config: &config,
|
||||
VariableValues: variables,
|
||||
},
|
||||
|
||||
&EvalCoerceMapVariable{
|
||||
Variables: variables,
|
||||
ModulePath: n.PathValue,
|
||||
ModuleTree: n.Module,
|
||||
},
|
||||
|
||||
&EvalTypeCheckVariable{
|
||||
Variables: variables,
|
||||
ModulePath: n.PathValue,
|
||||
ModuleTree: n.Module,
|
||||
},
|
||||
|
||||
&EvalSetVariables{
|
||||
Module: &n.PathValue[len(n.PathValue)-1],
|
||||
Variables: variables,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -25,6 +25,22 @@ type GraphNodeReferencer interface {
|
|||
References() []string
|
||||
}
|
||||
|
||||
// GraphNodeReferenceGlobal is an interface that can optionally be
|
||||
// implemented. If ReferenceGlobal returns true, then the References()
|
||||
// and ReferenceableName() must be _fully qualified_ with "module.foo.bar"
|
||||
// etc.
|
||||
//
|
||||
// This allows a node to reference and be referenced by a specific name
|
||||
// that may cross module boundaries. This can be very dangerous so use
|
||||
// this wisely.
|
||||
//
|
||||
// The primary use case for this is module boundaries (variables coming in).
|
||||
type GraphNodeReferenceGlobal interface {
|
||||
// Set to true to signal that references and name are fully
|
||||
// qualified. See the above docs for more information.
|
||||
ReferenceGlobal() bool
|
||||
}
|
||||
|
||||
// ReferenceTransformer is a GraphTransformer that connects all the
|
||||
// nodes that reference each other in order to form the proper ordering.
|
||||
type ReferenceTransformer struct{}
|
||||
|
@ -61,16 +77,9 @@ func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// If this node represents a sub path then we prefix
|
||||
var prefix string
|
||||
if pn, ok := v.(GraphNodeSubPath); ok {
|
||||
if path := normalizeModulePath(pn.Path()); len(path) > 1 {
|
||||
prefix = modulePrefixStr(path) + "."
|
||||
}
|
||||
}
|
||||
|
||||
var matches []dag.Vertex
|
||||
var missing []string
|
||||
prefix := m.prefix(v)
|
||||
for _, n := range rn.References() {
|
||||
n = prefix + n
|
||||
parents, ok := m.m[n]
|
||||
|
@ -85,9 +94,29 @@ func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) {
|
|||
return matches, missing
|
||||
}
|
||||
|
||||
func (m *ReferenceMap) prefix(v dag.Vertex) string {
|
||||
// If the node is stating it is already fully qualified then
|
||||
// we don't have to create the prefix!
|
||||
if gn, ok := v.(GraphNodeReferenceGlobal); ok && gn.ReferenceGlobal() {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Create the prefix based on the path
|
||||
var prefix string
|
||||
if pn, ok := v.(GraphNodeSubPath); ok {
|
||||
if path := normalizeModulePath(pn.Path()); len(path) > 1 {
|
||||
prefix = modulePrefixStr(path) + "."
|
||||
}
|
||||
}
|
||||
|
||||
return prefix
|
||||
}
|
||||
|
||||
// NewReferenceMap is used to create a new reference map for the
|
||||
// given set of vertices.
|
||||
func NewReferenceMap(vs []dag.Vertex) *ReferenceMap {
|
||||
var m ReferenceMap
|
||||
|
||||
// Build the lookup table
|
||||
refMap := make(map[string][]dag.Vertex)
|
||||
for _, v := range vs {
|
||||
|
@ -97,22 +126,16 @@ func NewReferenceMap(vs []dag.Vertex) *ReferenceMap {
|
|||
continue
|
||||
}
|
||||
|
||||
// If this node represents a sub path then we prefix
|
||||
var prefix string
|
||||
if pn, ok := v.(GraphNodeSubPath); ok {
|
||||
if path := normalizeModulePath(pn.Path()); len(path) > 1 {
|
||||
prefix = modulePrefixStr(path) + "."
|
||||
}
|
||||
}
|
||||
|
||||
// Go through and cache them
|
||||
prefix := m.prefix(v)
|
||||
for _, n := range rn.ReferenceableName() {
|
||||
n = prefix + n
|
||||
refMap[n] = append(refMap[n], v)
|
||||
}
|
||||
}
|
||||
|
||||
return &ReferenceMap{m: refMap}
|
||||
m.m = refMap
|
||||
return &m
|
||||
}
|
||||
|
||||
// ReferencesFromConfig returns the references that a configuration has
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
)
|
||||
|
||||
// VariableTransformer is a GraphTransformer that adds all the variables
|
||||
// in the configuration to the graph.
|
||||
//
|
||||
// This only adds variables that either have no dependencies (and therefore
|
||||
// always succeed) or has dependencies that are 100% represented in the
|
||||
// graph.
|
||||
type VariableTransformer struct {
|
||||
Module *module.Tree
|
||||
}
|
||||
|
||||
func (t *VariableTransformer) Transform(g *Graph) error {
|
||||
return t.transform(g, nil, t.Module)
|
||||
}
|
||||
|
||||
func (t *VariableTransformer) transform(g *Graph, parent, m *module.Tree) error {
|
||||
// If no config, no variables
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transform all the children.
|
||||
for _, c := range m.Children() {
|
||||
if err := t.transform(g, m, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no parent, then don't do anything. This is because
|
||||
// we need to be able to get the set value from the module declaration.
|
||||
if parent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we have no vars, we're done!
|
||||
vars := m.Config().Variables
|
||||
if len(vars) == 0 {
|
||||
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 %q not used, not adding variables", m.Name())
|
||||
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 := &NodeApplyableVariable{
|
||||
PathValue: normalizeModulePath(m.Path()),
|
||||
Config: v,
|
||||
Value: value,
|
||||
Module: t.Module,
|
||||
}
|
||||
|
||||
// If the node references something, then we check to make sure
|
||||
// that the thing it references is in the graph. If it isn't, then
|
||||
// we don't add it because we may not be able to compute the output.
|
||||
//
|
||||
// If the node references nothing, we always include it since there
|
||||
// is no other clear time to compute it.
|
||||
matches, missing := refMap.References(node)
|
||||
if len(missing) > 0 {
|
||||
log.Printf(
|
||||
"[INFO] Not including %q in graph, matches: %v, missing: %s",
|
||||
dag.VertexName(node), matches, missing)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add it!
|
||||
g.Add(node)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue