package earlyconfig import ( "fmt" "sort" version "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-config-inspect/tfconfig" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/moduledeps" "github.com/hashicorp/terraform/plugin/discovery" "github.com/hashicorp/terraform/tfdiags" ) // A Config is a node in the tree of modules within a configuration. // // The module tree is constructed by following ModuleCall instances recursively // through the root module transitively into descendent modules. type Config struct { // RootModule points to the Config for the root module within the same // module tree as this module. If this module _is_ the root module then // this is self-referential. Root *Config // ParentModule points to the Config for the module that directly calls // this module. If this is the root module then this field is nil. Parent *Config // Path is a sequence of module logical names that traverse from the root // module to this config. Path is empty for the root module. // // This should only be used to display paths to the end-user in rare cases // where we are talking about the static module tree, before module calls // have been resolved. In most cases, an addrs.ModuleInstance describing // a node in the dynamic module tree is better, since it will then include // any keys resulting from evaluating "count" and "for_each" arguments. Path addrs.Module // ChildModules points to the Config for each of the direct child modules // called from this module. The keys in this map match the keys in // Module.ModuleCalls. Children map[string]*Config // Module points to the object describing the configuration for the // various elements (variables, resources, etc) defined by this module. Module *tfconfig.Module // CallPos is the source position for the header of the module block that // requested this module. // // This field is meaningless for the root module, where its contents are undefined. CallPos tfconfig.SourcePos // SourceAddr is the source address that the referenced module was requested // from, as specified in configuration. // // This field is meaningless for the root module, where its contents are undefined. SourceAddr string // Version is the specific version that was selected for this module, // based on version constraints given in configuration. // // This field is nil if the module was loaded from a non-registry source, // since versions are not supported for other sources. // // This field is meaningless for the root module, where it will always // be nil. Version *version.Version } // ProviderDependencies returns the provider dependencies for the recieving // config, including all of its descendent modules. func (c *Config) ProviderDependencies() (*moduledeps.Module, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics var name string if len(c.Path) > 0 { name = c.Path[len(c.Path)-1] } ret := &moduledeps.Module{ Name: name, } providers := make(moduledeps.Providers) for name, reqs := range c.Module.RequiredProviders { inst := moduledeps.ProviderInstance(name) var constraints version.Constraints for _, reqStr := range reqs.VersionConstraints { if reqStr != "" { constraint, err := version.NewConstraint(reqStr) if err != nil { diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{ Severity: tfconfig.DiagError, Summary: "Invalid provider version constraint", Detail: fmt.Sprintf("Invalid version constraint %q for provider %s.", reqStr, name), })) continue } constraints = append(constraints, constraint...) } } providers[inst] = moduledeps.ProviderDependency{ Constraints: discovery.NewConstraints(constraints), Reason: moduledeps.ProviderDependencyExplicit, } } ret.Providers = providers childNames := make([]string, 0, len(c.Children)) for name := range c.Children { childNames = append(childNames, name) } sort.Strings(childNames) for _, name := range childNames { child, childDiags := c.Children[name].ProviderDependencies() ret.Children = append(ret.Children, child) diags = diags.Append(childDiags) } return ret, diags }