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  }