github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/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 &EvalCoerceMapVariable{ 183 Variables: variables, 184 ModulePath: n.ModulePath, 185 ModuleTree: n.ModuleTree, 186 }, 187 188 &EvalTypeCheckVariable{ 189 Variables: variables, 190 ModulePath: n.ModulePath, 191 ModuleTree: n.ModuleTree, 192 }, 193 194 &EvalSetVariables{ 195 Module: &n.Module, 196 Variables: variables, 197 }, 198 }, 199 } 200 } 201 202 // GraphNodeFlattenable impl. 203 func (n *GraphNodeConfigVariable) Flatten(p []string) (dag.Vertex, error) { 204 return &GraphNodeConfigVariableFlat{ 205 GraphNodeConfigVariable: n, 206 PathValue: p, 207 }, nil 208 } 209 210 type GraphNodeConfigVariableFlat struct { 211 *GraphNodeConfigVariable 212 213 PathValue []string 214 } 215 216 func (n *GraphNodeConfigVariableFlat) Name() string { 217 return fmt.Sprintf( 218 "%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigVariable.Name()) 219 } 220 221 func (n *GraphNodeConfigVariableFlat) DependableName() []string { 222 return []string{n.Name()} 223 } 224 225 func (n *GraphNodeConfigVariableFlat) DependentOn() []string { 226 // We only wrap the dependencies and such if we have a path that is 227 // longer than 2 elements (root, child, more). This is because when 228 // flattened, variables can point outside the graph. 229 prefix := "" 230 if len(n.PathValue) > 2 { 231 prefix = modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) 232 } 233 234 return modulePrefixList( 235 n.GraphNodeConfigVariable.DependentOn(), 236 prefix) 237 } 238 239 func (n *GraphNodeConfigVariableFlat) Path() []string { 240 if len(n.PathValue) > 2 { 241 return n.PathValue[:len(n.PathValue)-1] 242 } 243 244 return nil 245 } 246 247 func (n *GraphNodeConfigVariableFlat) Noop(opts *NoopOpts) bool { 248 // First look for provider nodes that depend on this variable downstream 249 modDiff := opts.Diff.ModuleByPath(n.ModulePath) 250 if modDiff != nil && modDiff.Destroy { 251 ds, err := opts.Graph.Descendents(n) 252 if err != nil { 253 log.Printf("[ERROR] Error looking up descendents of %s: %s", n.Name(), err) 254 } else { 255 for _, d := range ds.List() { 256 if _, ok := d.(GraphNodeProvider); ok { 257 log.Printf("[DEBUG] This variable is depended on by a provider, can't be a noop.") 258 return false 259 } 260 } 261 } 262 } 263 264 // Then fall back to existing impl 265 return n.GraphNodeConfigVariable.Noop(opts) 266 }