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 }