119 lines
2.8 KiB
Go
119 lines
2.8 KiB
Go
package module
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/terraform/dag"
|
|
)
|
|
|
|
// validateProviderAlias validates that all provider alias references are
|
|
// defined at some point in the parent tree. This improves UX by catching
|
|
// alias typos at the slight cost of requiring a declaration of usage. This
|
|
// is usually a good tradeoff since not many aliases are used.
|
|
func (t *Tree) validateProviderAlias() error {
|
|
// If we're not the root, don't perform this validation. We must be the
|
|
// root since we require full tree visibilty.
|
|
if len(t.path) != 0 {
|
|
return nil
|
|
}
|
|
|
|
// We'll use a graph to keep track of defined aliases at each level.
|
|
// As long as a parent defines an alias, it is okay.
|
|
var g dag.AcyclicGraph
|
|
t.buildProviderAliasGraph(&g, nil)
|
|
|
|
// Go through the graph and check that the usage is all good.
|
|
var err error
|
|
for _, v := range g.Vertices() {
|
|
pv, ok := v.(*providerAliasVertex)
|
|
if !ok {
|
|
// This shouldn't happen, just ignore it.
|
|
continue
|
|
}
|
|
|
|
// If we're not using any aliases, fast track and just continue
|
|
if len(pv.Used) == 0 {
|
|
continue
|
|
}
|
|
|
|
// Grab the ancestors since we're going to have to check if our
|
|
// parents define any of our aliases.
|
|
var parents []*providerAliasVertex
|
|
ancestors, _ := g.Ancestors(v)
|
|
for _, raw := range ancestors.List() {
|
|
if pv, ok := raw.(*providerAliasVertex); ok {
|
|
parents = append(parents, pv)
|
|
}
|
|
}
|
|
for k, _ := range pv.Used {
|
|
// Check if we define this
|
|
if _, ok := pv.Defined[k]; ok {
|
|
continue
|
|
}
|
|
|
|
// Check for a parent
|
|
found := false
|
|
for _, parent := range parents {
|
|
_, found = parent.Defined[k]
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
continue
|
|
}
|
|
|
|
// We didn't find the alias, error!
|
|
err = multierror.Append(err, fmt.Errorf(
|
|
"module %s: provider alias must be defined by the module: %s",
|
|
strings.Join(pv.Path, "."), k))
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (t *Tree) buildProviderAliasGraph(g *dag.AcyclicGraph, parent dag.Vertex) {
|
|
// Add all our defined aliases
|
|
defined := make(map[string]struct{})
|
|
for _, p := range t.config.ProviderConfigs {
|
|
defined[p.FullName()] = struct{}{}
|
|
}
|
|
|
|
// Add all our used aliases
|
|
used := make(map[string]struct{})
|
|
for _, r := range t.config.Resources {
|
|
if r.Provider != "" {
|
|
used[r.Provider] = struct{}{}
|
|
}
|
|
}
|
|
|
|
// Add it to the graph
|
|
vertex := &providerAliasVertex{
|
|
Path: t.Path(),
|
|
Defined: defined,
|
|
Used: used,
|
|
}
|
|
g.Add(vertex)
|
|
|
|
// Connect to our parent if we have one
|
|
if parent != nil {
|
|
g.Connect(dag.BasicEdge(vertex, parent))
|
|
}
|
|
|
|
// Build all our children
|
|
for _, c := range t.Children() {
|
|
c.buildProviderAliasGraph(g, vertex)
|
|
}
|
|
}
|
|
|
|
// providerAliasVertex is the vertex for the graph that keeps track of
|
|
// defined provider aliases.
|
|
type providerAliasVertex struct {
|
|
Path []string
|
|
Defined map[string]struct{}
|
|
Used map[string]struct{}
|
|
}
|