github.com/kevinklinger/open_terraform@v1.3.6/noninternal/earlyconfig/config.go (about)

     1  package earlyconfig
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	version "github.com/hashicorp/go-version"
     8  	"github.com/hashicorp/terraform-config-inspect/tfconfig"
     9  	"github.com/kevinklinger/open_terraform/noninternal/addrs"
    10  	"github.com/kevinklinger/open_terraform/noninternal/getproviders"
    11  	"github.com/kevinklinger/open_terraform/noninternal/moduledeps"
    12  	"github.com/kevinklinger/open_terraform/noninternal/plugin/discovery"
    13  	"github.com/kevinklinger/open_terraform/noninternal/tfdiags"
    14  )
    15  
    16  // A Config is a node in the tree of modules within a configuration.
    17  //
    18  // The module tree is constructed by following ModuleCall instances recursively
    19  // through the root module transitively into descendent modules.
    20  type Config struct {
    21  	// RootModule points to the Config for the root module within the same
    22  	// module tree as this module. If this module _is_ the root module then
    23  	// this is self-referential.
    24  	Root *Config
    25  
    26  	// ParentModule points to the Config for the module that directly calls
    27  	// this module. If this is the root module then this field is nil.
    28  	Parent *Config
    29  
    30  	// Path is a sequence of module logical names that traverse from the root
    31  	// module to this config. Path is empty for the root module.
    32  	//
    33  	// This should only be used to display paths to the end-user in rare cases
    34  	// where we are talking about the static module tree, before module calls
    35  	// have been resolved. In most cases, an addrs.ModuleInstance describing
    36  	// a node in the dynamic module tree is better, since it will then include
    37  	// any keys resulting from evaluating "count" and "for_each" arguments.
    38  	Path addrs.Module
    39  
    40  	// ChildModules points to the Config for each of the direct child modules
    41  	// called from this module. The keys in this map match the keys in
    42  	// Module.ModuleCalls.
    43  	Children map[string]*Config
    44  
    45  	// Module points to the object describing the configuration for the
    46  	// various elements (variables, resources, etc) defined by this module.
    47  	Module *tfconfig.Module
    48  
    49  	// CallPos is the source position for the header of the module block that
    50  	// requested this module.
    51  	//
    52  	// This field is meaningless for the root module, where its contents are undefined.
    53  	CallPos tfconfig.SourcePos
    54  
    55  	// SourceAddr is the source address that the referenced module was requested
    56  	// from, as specified in configuration.
    57  	//
    58  	// This field is meaningless for the root module, where its contents are undefined.
    59  	SourceAddr addrs.ModuleSource
    60  
    61  	// Version is the specific version that was selected for this module,
    62  	// based on version constraints given in configuration.
    63  	//
    64  	// This field is nil if the module was loaded from a non-registry source,
    65  	// since versions are not supported for other sources.
    66  	//
    67  	// This field is meaningless for the root module, where it will always
    68  	// be nil.
    69  	Version *version.Version
    70  }
    71  
    72  // ProviderRequirements searches the full tree of modules under the receiver
    73  // for both explicit and implicit dependencies on providers.
    74  //
    75  // The result is a full manifest of all of the providers that must be available
    76  // in order to work with the receiving configuration.
    77  //
    78  // If the returned diagnostics includes errors then the resulting Requirements
    79  // may be incomplete.
    80  func (c *Config) ProviderRequirements() (getproviders.Requirements, tfdiags.Diagnostics) {
    81  	reqs := make(getproviders.Requirements)
    82  	diags := c.addProviderRequirements(reqs)
    83  	return reqs, diags
    84  }
    85  
    86  // addProviderRequirements is the main part of the ProviderRequirements
    87  // implementation, gradually mutating a shared requirements object to
    88  // eventually return.
    89  func (c *Config) addProviderRequirements(reqs getproviders.Requirements) tfdiags.Diagnostics {
    90  	var diags tfdiags.Diagnostics
    91  
    92  	// First we'll deal with the requirements directly in _our_ module...
    93  	for localName, providerReqs := range c.Module.RequiredProviders {
    94  		var fqn addrs.Provider
    95  		if source := providerReqs.Source; source != "" {
    96  			addr, moreDiags := addrs.ParseProviderSourceString(source)
    97  			if moreDiags.HasErrors() {
    98  				diags = diags.Append(tfdiags.Sourceless(
    99  					tfdiags.Error,
   100  					"Invalid provider source address",
   101  					fmt.Sprintf("Invalid source %q for provider %q in %s", source, localName, c.Path),
   102  				))
   103  				continue
   104  			}
   105  			fqn = addr
   106  		}
   107  		if fqn.IsZero() {
   108  			fqn = addrs.ImpliedProviderForUnqualifiedType(localName)
   109  		}
   110  		if _, ok := reqs[fqn]; !ok {
   111  			// We'll at least have an unconstrained dependency then, but might
   112  			// add to this in the loop below.
   113  			reqs[fqn] = nil
   114  		}
   115  		for _, constraintsStr := range providerReqs.VersionConstraints {
   116  			if constraintsStr != "" {
   117  				constraints, err := getproviders.ParseVersionConstraints(constraintsStr)
   118  				if err != nil {
   119  					diags = diags.Append(tfdiags.Sourceless(
   120  						tfdiags.Error,
   121  						"Invalid provider version constraint",
   122  						fmt.Sprintf("Provider %q in %s has invalid version constraint %q: %s.", localName, c.Path, constraintsStr, err),
   123  					))
   124  					continue
   125  				}
   126  				reqs[fqn] = append(reqs[fqn], constraints...)
   127  			}
   128  		}
   129  	}
   130  
   131  	// ...and now we'll recursively visit all of the child modules to merge
   132  	// in their requirements too.
   133  	for _, childConfig := range c.Children {
   134  		moreDiags := childConfig.addProviderRequirements(reqs)
   135  		diags = diags.Append(moreDiags)
   136  	}
   137  
   138  	return diags
   139  }
   140  
   141  // ProviderDependencies is a deprecated variant of ProviderRequirements which
   142  // uses the moduledeps models for representation. This is preserved to allow
   143  // a gradual transition over to ProviderRequirements, but note that its
   144  // support for fully-qualified provider addresses has some idiosyncracies.
   145  func (c *Config) ProviderDependencies() (*moduledeps.Module, tfdiags.Diagnostics) {
   146  	var diags tfdiags.Diagnostics
   147  
   148  	var name string
   149  	if len(c.Path) > 0 {
   150  		name = c.Path[len(c.Path)-1]
   151  	}
   152  
   153  	ret := &moduledeps.Module{
   154  		Name: name,
   155  	}
   156  
   157  	providers := make(moduledeps.Providers)
   158  	for name, reqs := range c.Module.RequiredProviders {
   159  		var fqn addrs.Provider
   160  		if source := reqs.Source; source != "" {
   161  			addr, parseDiags := addrs.ParseProviderSourceString(source)
   162  			if parseDiags.HasErrors() {
   163  				diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{
   164  					Severity: tfconfig.DiagError,
   165  					Summary:  "Invalid provider source",
   166  					Detail:   fmt.Sprintf("Invalid source %q for provider", name),
   167  				}))
   168  				continue
   169  			}
   170  			fqn = addr
   171  		}
   172  		if fqn.IsZero() {
   173  			fqn = addrs.NewDefaultProvider(name)
   174  		}
   175  		var constraints version.Constraints
   176  		for _, reqStr := range reqs.VersionConstraints {
   177  			if reqStr != "" {
   178  				constraint, err := version.NewConstraint(reqStr)
   179  				if err != nil {
   180  					diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{
   181  						Severity: tfconfig.DiagError,
   182  						Summary:  "Invalid provider version constraint",
   183  						Detail:   fmt.Sprintf("Invalid version constraint %q for provider %s.", reqStr, fqn.String()),
   184  					}))
   185  					continue
   186  				}
   187  				constraints = append(constraints, constraint...)
   188  			}
   189  		}
   190  		providers[fqn] = moduledeps.ProviderDependency{
   191  			Constraints: discovery.NewConstraints(constraints),
   192  			Reason:      moduledeps.ProviderDependencyExplicit,
   193  		}
   194  	}
   195  	ret.Providers = providers
   196  
   197  	childNames := make([]string, 0, len(c.Children))
   198  	for name := range c.Children {
   199  		childNames = append(childNames, name)
   200  	}
   201  	sort.Strings(childNames)
   202  
   203  	for _, name := range childNames {
   204  		child, childDiags := c.Children[name].ProviderDependencies()
   205  		ret.Children = append(ret.Children, child)
   206  		diags = diags.Append(childDiags)
   207  	}
   208  
   209  	return ret, diags
   210  }