github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/moduledeps/module.go (about)

     1  package moduledeps
     2  
     3  import (
     4  	"sort"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery"
     8  )
     9  
    10  // Module represents the dependencies of a single module, as well being
    11  // a node in a tree of such structures representing the dependencies of
    12  // an entire configuration.
    13  type Module struct {
    14  	Name      string
    15  	Providers Providers
    16  	Children  []*Module
    17  }
    18  
    19  // WalkFunc is a callback type for use with Module.WalkTree
    20  type WalkFunc func(path []string, parent *Module, current *Module) error
    21  
    22  // WalkTree calls the given callback once for the receiver and then
    23  // once for each descendent, in an order such that parents are called
    24  // before their children and siblings are called in the order they
    25  // appear in the Children slice.
    26  //
    27  // When calling the callback, parent will be nil for the first call
    28  // for the receiving module, and then set to the direct parent of
    29  // each module for the subsequent calls.
    30  //
    31  // The path given to the callback is valid only until the callback
    32  // returns, after which it will be mutated and reused. Callbacks must
    33  // therefore copy the path slice if they wish to retain it.
    34  //
    35  // If the given callback returns an error, the walk will be aborted at
    36  // that point and that error returned to the caller.
    37  //
    38  // This function is not thread-safe for concurrent modifications of the
    39  // data structure, so it's the caller's responsibility to arrange for that
    40  // should it be needed.
    41  //
    42  // It is safe for a callback to modify the descendents of the "current"
    43  // module, including the ordering of the Children slice itself, but the
    44  // callback MUST NOT modify the parent module.
    45  func (m *Module) WalkTree(cb WalkFunc) error {
    46  	return walkModuleTree(make([]string, 0, 1), nil, m, cb)
    47  }
    48  
    49  func walkModuleTree(path []string, parent *Module, current *Module, cb WalkFunc) error {
    50  	path = append(path, current.Name)
    51  	err := cb(path, parent, current)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	for _, child := range current.Children {
    57  		err := walkModuleTree(path, current, child, cb)
    58  		if err != nil {
    59  			return err
    60  		}
    61  	}
    62  	return nil
    63  }
    64  
    65  // SortChildren sorts the Children slice into lexicographic order by
    66  // name, in-place.
    67  //
    68  // This is primarily useful prior to calling WalkTree so that the walk
    69  // will proceed in a consistent order.
    70  func (m *Module) SortChildren() {
    71  	sort.Sort(sortModules{m.Children})
    72  }
    73  
    74  type sortModules struct {
    75  	modules []*Module
    76  }
    77  
    78  func (s sortModules) Len() int {
    79  	return len(s.modules)
    80  }
    81  
    82  func (s sortModules) Less(i, j int) bool {
    83  	cmp := strings.Compare(s.modules[i].Name, s.modules[j].Name)
    84  	return cmp < 0
    85  }
    86  
    87  func (s sortModules) Swap(i, j int) {
    88  	s.modules[i], s.modules[j] = s.modules[j], s.modules[i]
    89  }
    90  
    91  // PluginRequirements produces a PluginRequirements structure that can
    92  // be used with discovery.PluginMetaSet.ConstrainVersions to identify
    93  // suitable plugins to satisfy the module's provider dependencies.
    94  //
    95  // This method only considers the direct requirements of the receiver.
    96  // Use AllPluginRequirements to flatten the dependencies for the
    97  // entire tree of modules.
    98  //
    99  // Requirements returned by this method include only version constraints,
   100  // and apply no particular SHA256 hash constraint.
   101  func (m *Module) PluginRequirements() discovery.PluginRequirements {
   102  	ret := make(discovery.PluginRequirements)
   103  	for inst, dep := range m.Providers {
   104  		// m.Providers is keyed on provider names, such as "aws.foo".
   105  		// a PluginRequirements wants keys to be provider *types*, such
   106  		// as "aws". If there are multiple aliases for the same
   107  		// provider then we will flatten them into a single requirement
   108  		// by combining their constraint sets.
   109  		pty := inst.Type()
   110  		if existing, exists := ret[pty]; exists {
   111  			ret[pty].Versions = existing.Versions.Append(dep.Constraints)
   112  		} else {
   113  			ret[pty] = &discovery.PluginConstraints{
   114  				Versions: dep.Constraints,
   115  			}
   116  		}
   117  	}
   118  	return ret
   119  }
   120  
   121  // AllPluginRequirements calls PluginRequirements for the receiver and all
   122  // of its descendents, and merges the result into a single PluginRequirements
   123  // structure that would satisfy all of the modules together.
   124  //
   125  // Requirements returned by this method include only version constraints,
   126  // and apply no particular SHA256 hash constraint.
   127  func (m *Module) AllPluginRequirements() discovery.PluginRequirements {
   128  	var ret discovery.PluginRequirements
   129  	m.WalkTree(func(path []string, parent *Module, current *Module) error {
   130  		ret = ret.Merge(current.PluginRequirements())
   131  		return nil
   132  	})
   133  	return ret
   134  }