github.com/memsql/terraform@v0.7.0-rc2.0.20160706152241-21e2173e0a32/terraform/graph_config_node_variable.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform/config"
     8  	"github.com/hashicorp/terraform/config/module"
     9  	"github.com/hashicorp/terraform/dag"
    10  )
    11  
    12  // GraphNodeConfigVariable represents a Variable in the config.
    13  type GraphNodeConfigVariable struct {
    14  	Variable *config.Variable
    15  
    16  	// Value, if non-nil, will be used to set the value of the variable
    17  	// during evaluation. If this is nil, evaluation will do nothing.
    18  	//
    19  	// Module is the name of the module to set the variables on.
    20  	Module string
    21  	Value  *config.RawConfig
    22  
    23  	ModuleTree *module.Tree
    24  	ModulePath []string
    25  }
    26  
    27  func (n *GraphNodeConfigVariable) Name() string {
    28  	return fmt.Sprintf("var.%s", n.Variable.Name)
    29  }
    30  
    31  func (n *GraphNodeConfigVariable) ConfigType() GraphNodeConfigType {
    32  	return GraphNodeConfigTypeVariable
    33  }
    34  
    35  func (n *GraphNodeConfigVariable) DependableName() []string {
    36  	return []string{n.Name()}
    37  }
    38  
    39  func (n *GraphNodeConfigVariable) DependentOn() []string {
    40  	// If we don't have any value set, we don't depend on anything
    41  	if n.Value == nil {
    42  		return nil
    43  	}
    44  
    45  	// Get what we depend on based on our value
    46  	vars := n.Value.Variables
    47  	result := make([]string, 0, len(vars))
    48  	for _, v := range vars {
    49  		if vn := varNameForVar(v); vn != "" {
    50  			result = append(result, vn)
    51  		}
    52  	}
    53  
    54  	return result
    55  }
    56  
    57  func (n *GraphNodeConfigVariable) VariableName() string {
    58  	return n.Variable.Name
    59  }
    60  
    61  // GraphNodeDestroyEdgeInclude impl.
    62  func (n *GraphNodeConfigVariable) DestroyEdgeInclude(v dag.Vertex) bool {
    63  	// Only include this variable in a destroy edge if the source vertex
    64  	// "v" has a count dependency on this variable.
    65  	log.Printf("[DEBUG] DestroyEdgeInclude: Checking: %s", dag.VertexName(v))
    66  	cv, ok := v.(GraphNodeCountDependent)
    67  	if !ok {
    68  		log.Printf("[DEBUG] DestroyEdgeInclude: Not GraphNodeCountDependent: %s", dag.VertexName(v))
    69  		return false
    70  	}
    71  
    72  	for _, d := range cv.CountDependentOn() {
    73  		for _, d2 := range n.DependableName() {
    74  			log.Printf("[DEBUG] DestroyEdgeInclude: d = %s : d2 = %s", d, d2)
    75  			if d == d2 {
    76  				return true
    77  			}
    78  		}
    79  	}
    80  
    81  	return false
    82  }
    83  
    84  // GraphNodeNoopPrunable
    85  func (n *GraphNodeConfigVariable) Noop(opts *NoopOpts) bool {
    86  	log.Printf("[DEBUG] Checking variable noop: %s", n.Name())
    87  	// If we have no diff, always keep this in the graph. We have to do
    88  	// this primarily for validation: we want to validate that variable
    89  	// interpolations are valid even if there are no resources that
    90  	// depend on them.
    91  	if opts.Diff == nil || opts.Diff.Empty() {
    92  		log.Printf("[DEBUG] No diff, not a noop")
    93  		return false
    94  	}
    95  
    96  	// We have to find our our module diff since we do funky things with
    97  	// the flat node's implementation of Path() below.
    98  	modDiff := opts.Diff.ModuleByPath(n.ModulePath)
    99  
   100  	// If we're destroying, we have no need of variables unless they are depended
   101  	// on by the count of a resource.
   102  	if modDiff != nil && modDiff.Destroy {
   103  		if n.hasDestroyEdgeInPath(opts, nil) {
   104  			log.Printf("[DEBUG] Variable has destroy edge from %s, not a noop",
   105  				dag.VertexName(opts.Vertex))
   106  			return false
   107  		}
   108  		log.Printf("[DEBUG] Variable has no included destroy edges: noop!")
   109  		return true
   110  	}
   111  
   112  	for _, v := range opts.Graph.UpEdges(opts.Vertex).List() {
   113  		// This is terrible, but I can't think of a better way to do this.
   114  		if dag.VertexName(v) == rootNodeName {
   115  			continue
   116  		}
   117  
   118  		log.Printf("[DEBUG] Found up edge to %s, var is not noop", dag.VertexName(v))
   119  		return false
   120  	}
   121  
   122  	log.Printf("[DEBUG] No up edges, treating variable as a noop")
   123  	return true
   124  }
   125  
   126  // hasDestroyEdgeInPath recursively walks for a destroy edge, ensuring that
   127  // a variable both has no immediate destroy edges or any in its full module
   128  // path, ensuring that links do not get severed in the middle.
   129  func (n *GraphNodeConfigVariable) hasDestroyEdgeInPath(opts *NoopOpts, vertex dag.Vertex) bool {
   130  	if vertex == nil {
   131  		vertex = opts.Vertex
   132  	}
   133  
   134  	log.Printf("[DEBUG] hasDestroyEdgeInPath: Looking for destroy edge: %s - %T", dag.VertexName(vertex), vertex)
   135  	for _, v := range opts.Graph.UpEdges(vertex).List() {
   136  		if len(opts.Graph.UpEdges(v).List()) > 1 {
   137  			if n.hasDestroyEdgeInPath(opts, v) == true {
   138  				return true
   139  			}
   140  		}
   141  
   142  		// Here we borrow the implementation of DestroyEdgeInclude, whose logic
   143  		// and semantics are exactly what we want here. We add a check for the
   144  		// the root node, since we have to always depend on its existance.
   145  		if cv, ok := vertex.(*GraphNodeConfigVariableFlat); ok {
   146  			if dag.VertexName(v) == rootNodeName || cv.DestroyEdgeInclude(v) {
   147  				return true
   148  			}
   149  		}
   150  	}
   151  	return false
   152  }
   153  
   154  // GraphNodeProxy impl.
   155  func (n *GraphNodeConfigVariable) Proxy() bool {
   156  	return true
   157  }
   158  
   159  // GraphNodeEvalable impl.
   160  func (n *GraphNodeConfigVariable) EvalTree() EvalNode {
   161  	// If we have no value, do nothing
   162  	if n.Value == nil {
   163  		return &EvalNoop{}
   164  	}
   165  
   166  	// Otherwise, interpolate the value of this variable and set it
   167  	// within the variables mapping.
   168  	var config *ResourceConfig
   169  	variables := make(map[string]interface{})
   170  	return &EvalSequence{
   171  		Nodes: []EvalNode{
   172  			&EvalInterpolate{
   173  				Config: n.Value,
   174  				Output: &config,
   175  			},
   176  
   177  			&EvalVariableBlock{
   178  				Config:         &config,
   179  				VariableValues: variables,
   180  			},
   181  
   182  			&EvalTypeCheckVariable{
   183  				Variables:  variables,
   184  				ModulePath: n.ModulePath,
   185  				ModuleTree: n.ModuleTree,
   186  			},
   187  
   188  			&EvalSetVariables{
   189  				Module:    &n.Module,
   190  				Variables: variables,
   191  			},
   192  		},
   193  	}
   194  }
   195  
   196  // GraphNodeFlattenable impl.
   197  func (n *GraphNodeConfigVariable) Flatten(p []string) (dag.Vertex, error) {
   198  	return &GraphNodeConfigVariableFlat{
   199  		GraphNodeConfigVariable: n,
   200  		PathValue:               p,
   201  	}, nil
   202  }
   203  
   204  type GraphNodeConfigVariableFlat struct {
   205  	*GraphNodeConfigVariable
   206  
   207  	PathValue []string
   208  }
   209  
   210  func (n *GraphNodeConfigVariableFlat) Name() string {
   211  	return fmt.Sprintf(
   212  		"%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigVariable.Name())
   213  }
   214  
   215  func (n *GraphNodeConfigVariableFlat) DependableName() []string {
   216  	return []string{n.Name()}
   217  }
   218  
   219  func (n *GraphNodeConfigVariableFlat) DependentOn() []string {
   220  	// We only wrap the dependencies and such if we have a path that is
   221  	// longer than 2 elements (root, child, more). This is because when
   222  	// flattened, variables can point outside the graph.
   223  	prefix := ""
   224  	if len(n.PathValue) > 2 {
   225  		prefix = modulePrefixStr(n.PathValue[:len(n.PathValue)-1])
   226  	}
   227  
   228  	return modulePrefixList(
   229  		n.GraphNodeConfigVariable.DependentOn(),
   230  		prefix)
   231  }
   232  
   233  func (n *GraphNodeConfigVariableFlat) Path() []string {
   234  	if len(n.PathValue) > 2 {
   235  		return n.PathValue[:len(n.PathValue)-1]
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  func (n *GraphNodeConfigVariableFlat) Noop(opts *NoopOpts) bool {
   242  	// First look for provider nodes that depend on this variable downstream
   243  	modDiff := opts.Diff.ModuleByPath(n.ModulePath)
   244  	if modDiff != nil && modDiff.Destroy {
   245  		ds, err := opts.Graph.Descendents(n)
   246  		if err != nil {
   247  			log.Printf("[ERROR] Error looking up descendents of %s: %s", n.Name(), err)
   248  		} else {
   249  			for _, d := range ds.List() {
   250  				if _, ok := d.(GraphNodeProvider); ok {
   251  					log.Printf("[DEBUG] This variable is depended on by a provider, can't be a noop.")
   252  					return false
   253  				}
   254  			}
   255  		}
   256  	}
   257  
   258  	// Then fall back to existing impl
   259  	return n.GraphNodeConfigVariable.Noop(opts)
   260  }