github.com/chentex/terraform@v0.11.2-0.20171208003256-252e8145842e/terraform/transform_reference.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/dag" 10 ) 11 12 // GraphNodeReferenceable must be implemented by any node that represents 13 // a Terraform thing that can be referenced (resource, module, etc.). 14 // 15 // Even if the thing has no name, this should return an empty list. By 16 // implementing this and returning a non-nil result, you say that this CAN 17 // be referenced and other methods of referencing may still be possible (such 18 // as by path!) 19 type GraphNodeReferenceable interface { 20 // ReferenceableName is the name by which this can be referenced. 21 // This can be either just the type, or include the field. Example: 22 // "aws_instance.bar" or "aws_instance.bar.id". 23 ReferenceableName() []string 24 } 25 26 // GraphNodeReferencer must be implemented by nodes that reference other 27 // Terraform items and therefore depend on them. 28 type GraphNodeReferencer interface { 29 // References are the list of things that this node references. This 30 // can include fields or just the type, just like GraphNodeReferenceable 31 // above. 32 References() []string 33 } 34 35 // GraphNodeReferenceGlobal is an interface that can optionally be 36 // implemented. If ReferenceGlobal returns true, then the References() 37 // and ReferenceableName() must be _fully qualified_ with "module.foo.bar" 38 // etc. 39 // 40 // This allows a node to reference and be referenced by a specific name 41 // that may cross module boundaries. This can be very dangerous so use 42 // this wisely. 43 // 44 // The primary use case for this is module boundaries (variables coming in). 45 type GraphNodeReferenceGlobal interface { 46 // Set to true to signal that references and name are fully 47 // qualified. See the above docs for more information. 48 ReferenceGlobal() bool 49 } 50 51 // ReferenceTransformer is a GraphTransformer that connects all the 52 // nodes that reference each other in order to form the proper ordering. 53 type ReferenceTransformer struct{} 54 55 func (t *ReferenceTransformer) Transform(g *Graph) error { 56 // Build a reference map so we can efficiently look up the references 57 vs := g.Vertices() 58 m := NewReferenceMap(vs) 59 60 // Find the things that reference things and connect them 61 for _, v := range vs { 62 parents, _ := m.References(v) 63 parentsDbg := make([]string, len(parents)) 64 for i, v := range parents { 65 parentsDbg[i] = dag.VertexName(v) 66 } 67 log.Printf( 68 "[DEBUG] ReferenceTransformer: %q references: %v", 69 dag.VertexName(v), parentsDbg) 70 71 for _, parent := range parents { 72 g.Connect(dag.BasicEdge(v, parent)) 73 } 74 } 75 76 return nil 77 } 78 79 // DestroyReferenceTransformer is a GraphTransformer that reverses the edges 80 // for nodes that depend on an Output or Local value. Output and local nodes are 81 // removed during destroy, so anything which depends on them must be evaluated 82 // first. These can't be interpolated during destroy, so the stored value must 83 // be used anyway hence they don't need to be re-evaluated. 84 type DestroyValueReferenceTransformer struct{} 85 86 func (t *DestroyValueReferenceTransformer) Transform(g *Graph) error { 87 vs := g.Vertices() 88 89 for _, v := range vs { 90 switch v.(type) { 91 case *NodeApplyableOutput, *NodeLocal: 92 // OK 93 default: 94 continue 95 } 96 97 // reverse any incoming edges so that the value is removed last 98 for _, e := range g.EdgesTo(v) { 99 source := e.Source() 100 log.Printf("[TRACE] output dep: %s", dag.VertexName(source)) 101 102 g.RemoveEdge(e) 103 g.Connect(&DestroyEdge{S: v, T: source}) 104 } 105 } 106 107 return nil 108 } 109 110 // ReferenceMap is a structure that can be used to efficiently check 111 // for references on a graph. 112 type ReferenceMap struct { 113 // m is the mapping of referenceable name to list of verticies that 114 // implement that name. This is built on initialization. 115 references map[string][]dag.Vertex 116 referencedBy map[string][]dag.Vertex 117 } 118 119 // References returns the list of vertices that this vertex 120 // references along with any missing references. 121 func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) { 122 rn, ok := v.(GraphNodeReferencer) 123 if !ok { 124 return nil, nil 125 } 126 127 var matches []dag.Vertex 128 var missing []string 129 prefix := m.prefix(v) 130 131 for _, ns := range rn.References() { 132 found := false 133 for _, n := range strings.Split(ns, "/") { 134 n = prefix + n 135 parents, ok := m.references[n] 136 if !ok { 137 continue 138 } 139 140 // Mark that we found a match 141 found = true 142 143 for _, p := range parents { 144 // don't include self-references 145 if p == v { 146 continue 147 } 148 matches = append(matches, p) 149 } 150 151 break 152 } 153 154 if !found { 155 missing = append(missing, ns) 156 } 157 } 158 159 return matches, missing 160 } 161 162 // ReferencedBy returns the list of vertices that reference the 163 // vertex passed in. 164 func (m *ReferenceMap) ReferencedBy(v dag.Vertex) []dag.Vertex { 165 rn, ok := v.(GraphNodeReferenceable) 166 if !ok { 167 return nil 168 } 169 170 var matches []dag.Vertex 171 prefix := m.prefix(v) 172 for _, n := range rn.ReferenceableName() { 173 n = prefix + n 174 children, ok := m.referencedBy[n] 175 if !ok { 176 continue 177 } 178 179 // Make sure this isn't a self reference, which isn't included 180 selfRef := false 181 for _, p := range children { 182 if p == v { 183 selfRef = true 184 break 185 } 186 } 187 if selfRef { 188 continue 189 } 190 191 matches = append(matches, children...) 192 } 193 194 return matches 195 } 196 197 func (m *ReferenceMap) prefix(v dag.Vertex) string { 198 // If the node is stating it is already fully qualified then 199 // we don't have to create the prefix! 200 if gn, ok := v.(GraphNodeReferenceGlobal); ok && gn.ReferenceGlobal() { 201 return "" 202 } 203 204 // Create the prefix based on the path 205 var prefix string 206 if pn, ok := v.(GraphNodeSubPath); ok { 207 if path := normalizeModulePath(pn.Path()); len(path) > 1 { 208 prefix = modulePrefixStr(path) + "." 209 } 210 } 211 212 return prefix 213 } 214 215 // NewReferenceMap is used to create a new reference map for the 216 // given set of vertices. 217 func NewReferenceMap(vs []dag.Vertex) *ReferenceMap { 218 var m ReferenceMap 219 220 // Build the lookup table 221 refMap := make(map[string][]dag.Vertex) 222 for _, v := range vs { 223 // We're only looking for referenceable nodes 224 rn, ok := v.(GraphNodeReferenceable) 225 if !ok { 226 continue 227 } 228 229 // Go through and cache them 230 prefix := m.prefix(v) 231 for _, n := range rn.ReferenceableName() { 232 n = prefix + n 233 refMap[n] = append(refMap[n], v) 234 } 235 236 // If there is a path, it is always referenceable by that. For 237 // example, if this is a referenceable thing at path []string{"foo"}, 238 // then it can be referenced at "module.foo" 239 if pn, ok := v.(GraphNodeSubPath); ok { 240 for _, p := range ReferenceModulePath(pn.Path()) { 241 refMap[p] = append(refMap[p], v) 242 } 243 } 244 } 245 246 // Build the lookup table for referenced by 247 refByMap := make(map[string][]dag.Vertex) 248 for _, v := range vs { 249 // We're only looking for referenceable nodes 250 rn, ok := v.(GraphNodeReferencer) 251 if !ok { 252 continue 253 } 254 255 // Go through and cache them 256 prefix := m.prefix(v) 257 for _, n := range rn.References() { 258 n = prefix + n 259 refByMap[n] = append(refByMap[n], v) 260 } 261 } 262 263 m.references = refMap 264 m.referencedBy = refByMap 265 return &m 266 } 267 268 // Returns the reference name for a module path. The path "foo" would return 269 // "module.foo". If this is a deeply nested module, it will be every parent 270 // as well. For example: ["foo", "bar"] would return both "module.foo" and 271 // "module.foo.module.bar" 272 func ReferenceModulePath(p []string) []string { 273 p = normalizeModulePath(p) 274 if len(p) == 1 { 275 // Root, no name 276 return nil 277 } 278 279 result := make([]string, 0, len(p)-1) 280 for i := len(p); i > 1; i-- { 281 result = append(result, modulePrefixStr(p[:i])) 282 } 283 284 return result 285 } 286 287 // ReferencesFromConfig returns the references that a configuration has 288 // based on the interpolated variables in a configuration. 289 func ReferencesFromConfig(c *config.RawConfig) []string { 290 var result []string 291 for _, v := range c.Variables { 292 if r := ReferenceFromInterpolatedVar(v); len(r) > 0 { 293 result = append(result, r...) 294 } 295 } 296 297 return result 298 } 299 300 // ReferenceFromInterpolatedVar returns the reference from this variable, 301 // or an empty string if there is no reference. 302 func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string { 303 switch v := v.(type) { 304 case *config.ModuleVariable: 305 return []string{fmt.Sprintf("module.%s.output.%s", v.Name, v.Field)} 306 case *config.ResourceVariable: 307 id := v.ResourceId() 308 309 // If we have a multi-reference (splat), then we depend on ALL 310 // resources with this type/name. 311 if v.Multi && v.Index == -1 { 312 return []string{fmt.Sprintf("%s.*", id)} 313 } 314 315 // Otherwise, we depend on a specific index. 316 idx := v.Index 317 if !v.Multi || v.Index == -1 { 318 idx = 0 319 } 320 321 // Depend on the index, as well as "N" which represents the 322 // un-expanded set of resources. 323 return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)} 324 case *config.UserVariable: 325 return []string{fmt.Sprintf("var.%s", v.Name)} 326 case *config.LocalVariable: 327 return []string{fmt.Sprintf("local.%s", v.Name)} 328 default: 329 return nil 330 } 331 } 332 333 func modulePrefixStr(p []string) string { 334 // strip "root" 335 if len(p) > 0 && p[0] == rootModulePath[0] { 336 p = p[1:] 337 } 338 339 parts := make([]string, 0, len(p)*2) 340 for _, p := range p { 341 parts = append(parts, "module", p) 342 } 343 344 return strings.Join(parts, ".") 345 } 346 347 func modulePrefixList(result []string, prefix string) []string { 348 if prefix != "" { 349 for i, v := range result { 350 result[i] = fmt.Sprintf("%s.%s", prefix, v) 351 } 352 } 353 354 return result 355 }