203 lines
4.9 KiB
Go
203 lines
4.9 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/config"
|
|
"github.com/hashicorp/terraform/config/module"
|
|
"github.com/hashicorp/terraform/dag"
|
|
)
|
|
|
|
// GraphNodeConfigVariable represents a Variable in the config.
|
|
type GraphNodeConfigVariable struct {
|
|
Variable *config.Variable
|
|
|
|
// Value, if non-nil, will be used to set the value of the variable
|
|
// during evaluation. If this is nil, evaluation will do nothing.
|
|
//
|
|
// Module is the name of the module to set the variables on.
|
|
Module string
|
|
Value *config.RawConfig
|
|
|
|
ModuleTree *module.Tree
|
|
ModulePath []string
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariable) Name() string {
|
|
return fmt.Sprintf("var.%s", n.Variable.Name)
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariable) ConfigType() GraphNodeConfigType {
|
|
return GraphNodeConfigTypeVariable
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariable) DependableName() []string {
|
|
return []string{n.Name()}
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariable) DependentOn() []string {
|
|
// If we don't have any value set, we don't depend on anything
|
|
if n.Value == nil {
|
|
return nil
|
|
}
|
|
|
|
// Get what we depend on based on our value
|
|
vars := n.Value.Variables
|
|
result := make([]string, 0, len(vars))
|
|
for _, v := range vars {
|
|
if vn := varNameForVar(v); vn != "" {
|
|
result = append(result, vn)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariable) VariableName() string {
|
|
return n.Variable.Name
|
|
}
|
|
|
|
// GraphNodeDestroyEdgeInclude impl.
|
|
func (n *GraphNodeConfigVariable) DestroyEdgeInclude(v dag.Vertex) bool {
|
|
// Only include this variable in a destroy edge if the source vertex
|
|
// "v" has a count dependency on this variable.
|
|
cv, ok := v.(GraphNodeCountDependent)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
for _, d := range cv.CountDependentOn() {
|
|
for _, d2 := range n.DependableName() {
|
|
if d == d2 {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// GraphNodeNoopPrunable
|
|
func (n *GraphNodeConfigVariable) Noop(opts *NoopOpts) bool {
|
|
log.Printf("[DEBUG] Checking variable noop: %s", n.Name())
|
|
// If we have no diff, always keep this in the graph. We have to do
|
|
// this primarily for validation: we want to validate that variable
|
|
// interpolations are valid even if there are no resources that
|
|
// depend on them.
|
|
if opts.Diff == nil || opts.Diff.Empty() {
|
|
log.Printf("[DEBUG] No diff, not a noop")
|
|
return false
|
|
}
|
|
|
|
// We have to find our our module diff since we do funky things with
|
|
// the flat node's implementation of Path() below.
|
|
modDiff := opts.Diff.ModuleByPath(n.ModulePath)
|
|
|
|
// If we're destroying, we have no need of variables.
|
|
if modDiff != nil && modDiff.Destroy {
|
|
log.Printf("[DEBUG] Destroy diff, treating variable as a noop")
|
|
return true
|
|
}
|
|
|
|
for _, v := range opts.Graph.UpEdges(opts.Vertex).List() {
|
|
// This is terrible, but I can't think of a better way to do this.
|
|
if dag.VertexName(v) == rootNodeName {
|
|
continue
|
|
}
|
|
|
|
log.Printf("[DEBUG] Found up edge to %s, var is not noop", dag.VertexName(v))
|
|
return false
|
|
}
|
|
|
|
log.Printf("[DEBUG] No up edges, treating variable as a noop")
|
|
return true
|
|
}
|
|
|
|
// GraphNodeProxy impl.
|
|
func (n *GraphNodeConfigVariable) Proxy() bool {
|
|
return true
|
|
}
|
|
|
|
// GraphNodeEvalable impl.
|
|
func (n *GraphNodeConfigVariable) 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,
|
|
},
|
|
|
|
&EvalTypeCheckVariable{
|
|
Variables: variables,
|
|
ModulePath: n.ModulePath,
|
|
ModuleTree: n.ModuleTree,
|
|
},
|
|
|
|
&EvalSetVariables{
|
|
Module: &n.Module,
|
|
Variables: variables,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GraphNodeFlattenable impl.
|
|
func (n *GraphNodeConfigVariable) Flatten(p []string) (dag.Vertex, error) {
|
|
return &GraphNodeConfigVariableFlat{
|
|
GraphNodeConfigVariable: n,
|
|
PathValue: p,
|
|
}, nil
|
|
}
|
|
|
|
type GraphNodeConfigVariableFlat struct {
|
|
*GraphNodeConfigVariable
|
|
|
|
PathValue []string
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariableFlat) Name() string {
|
|
return fmt.Sprintf(
|
|
"%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigVariable.Name())
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariableFlat) DependableName() []string {
|
|
return []string{n.Name()}
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariableFlat) DependentOn() []string {
|
|
// We only wrap the dependencies and such if we have a path that is
|
|
// longer than 2 elements (root, child, more). This is because when
|
|
// flattened, variables can point outside the graph.
|
|
prefix := ""
|
|
if len(n.PathValue) > 2 {
|
|
prefix = modulePrefixStr(n.PathValue[:len(n.PathValue)-1])
|
|
}
|
|
|
|
return modulePrefixList(
|
|
n.GraphNodeConfigVariable.DependentOn(),
|
|
prefix)
|
|
}
|
|
|
|
func (n *GraphNodeConfigVariableFlat) Path() []string {
|
|
if len(n.PathValue) > 2 {
|
|
return n.PathValue[:len(n.PathValue)-1]
|
|
}
|
|
|
|
return nil
|
|
}
|