github.com/ljosa/terraform@v0.7.0-rc2.0.20160617205345-fe540b408f59/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 log.Printf("[DEBUG] hasDestroyEdgeInPath: Looking for destroy edge: %s - %T", dag.VertexName(vertex), vertex) 134 for _, v := range opts.Graph.UpEdges(vertex).List() { 135 if len(opts.Graph.UpEdges(v).List()) > 1 { 136 if n.hasDestroyEdgeInPath(opts, v) == true { 137 return true 138 } 139 } 140 // Here we borrow the implementation of DestroyEdgeInclude, whose logic 141 // and semantics are exactly what we want here. 142 if cv, ok := vertex.(*GraphNodeConfigVariableFlat); ok { 143 if cv.DestroyEdgeInclude(v) { 144 return true 145 } 146 } 147 } 148 return false 149 } 150 151 // GraphNodeProxy impl. 152 func (n *GraphNodeConfigVariable) Proxy() bool { 153 return true 154 } 155 156 // GraphNodeEvalable impl. 157 func (n *GraphNodeConfigVariable) EvalTree() EvalNode { 158 // If we have no value, do nothing 159 if n.Value == nil { 160 return &EvalNoop{} 161 } 162 163 // Otherwise, interpolate the value of this variable and set it 164 // within the variables mapping. 165 var config *ResourceConfig 166 variables := make(map[string]interface{}) 167 return &EvalSequence{ 168 Nodes: []EvalNode{ 169 &EvalInterpolate{ 170 Config: n.Value, 171 Output: &config, 172 }, 173 174 &EvalVariableBlock{ 175 Config: &config, 176 VariableValues: variables, 177 }, 178 179 &EvalTypeCheckVariable{ 180 Variables: variables, 181 ModulePath: n.ModulePath, 182 ModuleTree: n.ModuleTree, 183 }, 184 185 &EvalSetVariables{ 186 Module: &n.Module, 187 Variables: variables, 188 }, 189 }, 190 } 191 } 192 193 // GraphNodeFlattenable impl. 194 func (n *GraphNodeConfigVariable) Flatten(p []string) (dag.Vertex, error) { 195 return &GraphNodeConfigVariableFlat{ 196 GraphNodeConfigVariable: n, 197 PathValue: p, 198 }, nil 199 } 200 201 type GraphNodeConfigVariableFlat struct { 202 *GraphNodeConfigVariable 203 204 PathValue []string 205 } 206 207 func (n *GraphNodeConfigVariableFlat) Name() string { 208 return fmt.Sprintf( 209 "%s.%s", modulePrefixStr(n.PathValue), n.GraphNodeConfigVariable.Name()) 210 } 211 212 func (n *GraphNodeConfigVariableFlat) DependableName() []string { 213 return []string{n.Name()} 214 } 215 216 func (n *GraphNodeConfigVariableFlat) DependentOn() []string { 217 // We only wrap the dependencies and such if we have a path that is 218 // longer than 2 elements (root, child, more). This is because when 219 // flattened, variables can point outside the graph. 220 prefix := "" 221 if len(n.PathValue) > 2 { 222 prefix = modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) 223 } 224 225 return modulePrefixList( 226 n.GraphNodeConfigVariable.DependentOn(), 227 prefix) 228 } 229 230 func (n *GraphNodeConfigVariableFlat) Path() []string { 231 if len(n.PathValue) > 2 { 232 return n.PathValue[:len(n.PathValue)-1] 233 } 234 235 return nil 236 } 237 238 func (n *GraphNodeConfigVariableFlat) Noop(opts *NoopOpts) bool { 239 // First look for provider nodes that depend on this variable downstream 240 modDiff := opts.Diff.ModuleByPath(n.ModulePath) 241 if modDiff != nil && modDiff.Destroy { 242 ds, err := opts.Graph.Descendents(n) 243 if err != nil { 244 log.Printf("[ERROR] Error looking up descendents of %s: %s", n.Name(), err) 245 } else { 246 for _, d := range ds.List() { 247 if _, ok := d.(GraphNodeProvider); ok { 248 log.Printf("[DEBUG] This variable is depended on by a provider, can't be a noop.") 249 return false 250 } 251 } 252 } 253 } 254 255 // Then fall back to existing impl 256 return n.GraphNodeConfigVariable.Noop(opts) 257 }