github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/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 }