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