github.com/medzin/terraform@v0.11.11/config/module/validate_provider_alias.go (about) 1 package module 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/go-multierror" 8 "github.com/hashicorp/terraform/dag" 9 ) 10 11 // validateProviderAlias validates that all provider alias references are 12 // defined at some point in the parent tree. This improves UX by catching 13 // alias typos at the slight cost of requiring a declaration of usage. This 14 // is usually a good tradeoff since not many aliases are used. 15 func (t *Tree) validateProviderAlias() error { 16 // If we're not the root, don't perform this validation. We must be the 17 // root since we require full tree visibilty. 18 if len(t.path) != 0 { 19 return nil 20 } 21 22 // We'll use a graph to keep track of defined aliases at each level. 23 // As long as a parent defines an alias, it is okay. 24 var g dag.AcyclicGraph 25 t.buildProviderAliasGraph(&g, nil) 26 27 // Go through the graph and check that the usage is all good. 28 var err error 29 for _, v := range g.Vertices() { 30 pv, ok := v.(*providerAliasVertex) 31 if !ok { 32 // This shouldn't happen, just ignore it. 33 continue 34 } 35 36 // If we're not using any aliases, fast track and just continue 37 if len(pv.Used) == 0 { 38 continue 39 } 40 41 // Grab the ancestors since we're going to have to check if our 42 // parents define any of our aliases. 43 var parents []*providerAliasVertex 44 ancestors, _ := g.Ancestors(v) 45 for _, raw := range ancestors.List() { 46 if pv, ok := raw.(*providerAliasVertex); ok { 47 parents = append(parents, pv) 48 } 49 } 50 for k, _ := range pv.Used { 51 // Check if we define this 52 if _, ok := pv.Defined[k]; ok { 53 continue 54 } 55 56 // Check for a parent 57 found := false 58 for _, parent := range parents { 59 _, found = parent.Defined[k] 60 if found { 61 break 62 } 63 } 64 if found { 65 continue 66 } 67 68 // We didn't find the alias, error! 69 err = multierror.Append(err, fmt.Errorf( 70 "module %s: provider alias must be defined by the module: %s", 71 strings.Join(pv.Path, "."), k)) 72 } 73 } 74 75 return err 76 } 77 78 func (t *Tree) buildProviderAliasGraph(g *dag.AcyclicGraph, parent dag.Vertex) { 79 // Add all our defined aliases 80 defined := make(map[string]struct{}) 81 for _, p := range t.config.ProviderConfigs { 82 defined[p.FullName()] = struct{}{} 83 } 84 85 // Add all our used aliases 86 used := make(map[string]struct{}) 87 for _, r := range t.config.Resources { 88 if r.Provider != "" { 89 used[r.Provider] = struct{}{} 90 } 91 } 92 93 // Add it to the graph 94 vertex := &providerAliasVertex{ 95 Path: t.Path(), 96 Defined: defined, 97 Used: used, 98 } 99 g.Add(vertex) 100 101 // Connect to our parent if we have one 102 if parent != nil { 103 g.Connect(dag.BasicEdge(vertex, parent)) 104 } 105 106 // Build all our children 107 for _, c := range t.Children() { 108 c.buildProviderAliasGraph(g, vertex) 109 } 110 } 111 112 // providerAliasVertex is the vertex for the graph that keeps track of 113 // defined provider aliases. 114 type providerAliasVertex struct { 115 Path []string 116 Defined map[string]struct{} 117 Used map[string]struct{} 118 }