github.com/mehmetalisavas/terraform@v0.7.10/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 type GraphNodeReferenceable interface { 15 // ReferenceableName is the name by which this can be referenced. 16 // This can be either just the type, or include the field. Example: 17 // "aws_instance.bar" or "aws_instance.bar.id". 18 ReferenceableName() []string 19 } 20 21 // GraphNodeReferencer must be implemented by nodes that reference other 22 // Terraform items and therefore depend on them. 23 type GraphNodeReferencer interface { 24 // References are the list of things that this node references. This 25 // can include fields or just the type, just like GraphNodeReferenceable 26 // above. 27 References() []string 28 } 29 30 // GraphNodeReferenceGlobal is an interface that can optionally be 31 // implemented. If ReferenceGlobal returns true, then the References() 32 // and ReferenceableName() must be _fully qualified_ with "module.foo.bar" 33 // etc. 34 // 35 // This allows a node to reference and be referenced by a specific name 36 // that may cross module boundaries. This can be very dangerous so use 37 // this wisely. 38 // 39 // The primary use case for this is module boundaries (variables coming in). 40 type GraphNodeReferenceGlobal interface { 41 // Set to true to signal that references and name are fully 42 // qualified. See the above docs for more information. 43 ReferenceGlobal() bool 44 } 45 46 // ReferenceTransformer is a GraphTransformer that connects all the 47 // nodes that reference each other in order to form the proper ordering. 48 type ReferenceTransformer struct{} 49 50 func (t *ReferenceTransformer) Transform(g *Graph) error { 51 // Build a reference map so we can efficiently look up the references 52 vs := g.Vertices() 53 m := NewReferenceMap(vs) 54 55 // Find the things that reference things and connect them 56 for _, v := range vs { 57 parents, _ := m.References(v) 58 parentsDbg := make([]string, len(parents)) 59 for i, v := range parents { 60 parentsDbg[i] = dag.VertexName(v) 61 } 62 log.Printf( 63 "[DEBUG] ReferenceTransformer: %q references: %v", 64 dag.VertexName(v), parentsDbg) 65 66 for _, parent := range parents { 67 g.Connect(dag.BasicEdge(v, parent)) 68 } 69 } 70 71 return nil 72 } 73 74 // ReferenceMap is a structure that can be used to efficiently check 75 // for references on a graph. 76 type ReferenceMap struct { 77 // m is the mapping of referenceable name to list of verticies that 78 // implement that name. This is built on initialization. 79 references map[string][]dag.Vertex 80 referencedBy map[string][]dag.Vertex 81 } 82 83 // References returns the list of vertices that this vertex 84 // references along with any missing references. 85 func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) { 86 rn, ok := v.(GraphNodeReferencer) 87 if !ok { 88 return nil, nil 89 } 90 91 var matches []dag.Vertex 92 var missing []string 93 prefix := m.prefix(v) 94 for _, ns := range rn.References() { 95 found := false 96 for _, n := range strings.Split(ns, "/") { 97 n = prefix + n 98 parents, ok := m.references[n] 99 if !ok { 100 continue 101 } 102 103 // Mark that we found a match 104 found = true 105 106 // Make sure this isn't a self reference, which isn't included 107 selfRef := false 108 for _, p := range parents { 109 if p == v { 110 selfRef = true 111 break 112 } 113 } 114 if selfRef { 115 continue 116 } 117 118 matches = append(matches, parents...) 119 break 120 } 121 122 if !found { 123 missing = append(missing, ns) 124 } 125 } 126 127 return matches, missing 128 } 129 130 // ReferencedBy returns the list of vertices that reference the 131 // vertex passed in. 132 func (m *ReferenceMap) ReferencedBy(v dag.Vertex) []dag.Vertex { 133 rn, ok := v.(GraphNodeReferenceable) 134 if !ok { 135 return nil 136 } 137 138 var matches []dag.Vertex 139 prefix := m.prefix(v) 140 for _, n := range rn.ReferenceableName() { 141 n = prefix + n 142 children, ok := m.referencedBy[n] 143 if !ok { 144 continue 145 } 146 147 // Make sure this isn't a self reference, which isn't included 148 selfRef := false 149 for _, p := range children { 150 if p == v { 151 selfRef = true 152 break 153 } 154 } 155 if selfRef { 156 continue 157 } 158 159 matches = append(matches, children...) 160 } 161 162 return matches 163 } 164 165 func (m *ReferenceMap) prefix(v dag.Vertex) string { 166 // If the node is stating it is already fully qualified then 167 // we don't have to create the prefix! 168 if gn, ok := v.(GraphNodeReferenceGlobal); ok && gn.ReferenceGlobal() { 169 return "" 170 } 171 172 // Create the prefix based on the path 173 var prefix string 174 if pn, ok := v.(GraphNodeSubPath); ok { 175 if path := normalizeModulePath(pn.Path()); len(path) > 1 { 176 prefix = modulePrefixStr(path) + "." 177 } 178 } 179 180 return prefix 181 } 182 183 // NewReferenceMap is used to create a new reference map for the 184 // given set of vertices. 185 func NewReferenceMap(vs []dag.Vertex) *ReferenceMap { 186 var m ReferenceMap 187 188 // Build the lookup table 189 refMap := make(map[string][]dag.Vertex) 190 for _, v := range vs { 191 // We're only looking for referenceable nodes 192 rn, ok := v.(GraphNodeReferenceable) 193 if !ok { 194 continue 195 } 196 197 // Go through and cache them 198 prefix := m.prefix(v) 199 for _, n := range rn.ReferenceableName() { 200 n = prefix + n 201 refMap[n] = append(refMap[n], v) 202 } 203 } 204 205 // Build the lookup table for referenced by 206 refByMap := make(map[string][]dag.Vertex) 207 for _, v := range vs { 208 // We're only looking for referenceable nodes 209 rn, ok := v.(GraphNodeReferencer) 210 if !ok { 211 continue 212 } 213 214 // Go through and cache them 215 prefix := m.prefix(v) 216 for _, n := range rn.References() { 217 n = prefix + n 218 refByMap[n] = append(refByMap[n], v) 219 } 220 } 221 222 m.references = refMap 223 m.referencedBy = refByMap 224 return &m 225 } 226 227 // ReferencesFromConfig returns the references that a configuration has 228 // based on the interpolated variables in a configuration. 229 func ReferencesFromConfig(c *config.RawConfig) []string { 230 var result []string 231 for _, v := range c.Variables { 232 if r := ReferenceFromInterpolatedVar(v); len(r) > 0 { 233 result = append(result, r...) 234 } 235 } 236 237 return result 238 } 239 240 // ReferenceFromInterpolatedVar returns the reference from this variable, 241 // or an empty string if there is no reference. 242 func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string { 243 switch v := v.(type) { 244 case *config.ModuleVariable: 245 return []string{fmt.Sprintf("module.%s.output.%s", v.Name, v.Field)} 246 case *config.ResourceVariable: 247 id := v.ResourceId() 248 249 // If we have a multi-reference (splat), then we depend on ALL 250 // resources with this type/name. 251 if v.Multi && v.Index == -1 { 252 return []string{fmt.Sprintf("%s.*", id)} 253 } 254 255 // Otherwise, we depend on a specific index. 256 idx := v.Index 257 if !v.Multi || v.Index == -1 { 258 idx = 0 259 } 260 261 // Depend on the index, as well as "N" which represents the 262 // un-expanded set of resources. 263 return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)} 264 case *config.UserVariable: 265 return []string{fmt.Sprintf("var.%s", v.Name)} 266 default: 267 return nil 268 } 269 }