157 lines
4.6 KiB
Go
157 lines
4.6 KiB
Go
package terraform
|
|
|
|
import (
|
|
"github.com/hashicorp/terraform/config"
|
|
"github.com/hashicorp/terraform/config/module"
|
|
"github.com/hashicorp/terraform/moduledeps"
|
|
"github.com/hashicorp/terraform/plugin/discovery"
|
|
)
|
|
|
|
// ModuleTreeDependencies returns the dependencies of the tree of modules
|
|
// described by the given configuration tree and state.
|
|
//
|
|
// Both configuration and state are required because there can be resources
|
|
// implied by instances in the state that no longer exist in config.
|
|
//
|
|
// This function will panic if any invalid version constraint strings are
|
|
// present in the configuration. This is guaranteed not to happen for any
|
|
// configuration that has passed a call to Config.Validate().
|
|
func ModuleTreeDependencies(root *module.Tree, state *State) *moduledeps.Module {
|
|
|
|
// First we walk the configuration tree to build the overall structure
|
|
// and capture the explicit/implicit/inherited provider dependencies.
|
|
deps := moduleTreeConfigDependencies(root, nil)
|
|
|
|
// Next we walk over the resources in the state to catch any additional
|
|
// dependencies created by existing resources that are no longer in config.
|
|
// Most things we find in state will already be present in 'deps', but
|
|
// we're interested in the rare thing that isn't.
|
|
moduleTreeMergeStateDependencies(deps, state)
|
|
|
|
return deps
|
|
}
|
|
|
|
func moduleTreeConfigDependencies(root *module.Tree, inheritProviders map[string]*config.ProviderConfig) *moduledeps.Module {
|
|
if root == nil {
|
|
// If no config is provided, we'll make a synthetic root.
|
|
// This isn't necessarily correct if we're called with a nil that
|
|
// *isn't* at the root, but in practice that can never happen.
|
|
return &moduledeps.Module{
|
|
Name: "root",
|
|
}
|
|
}
|
|
|
|
ret := &moduledeps.Module{
|
|
Name: root.Name(),
|
|
}
|
|
|
|
cfg := root.Config()
|
|
providerConfigs := cfg.ProviderConfigsByFullName()
|
|
|
|
// Provider dependencies
|
|
{
|
|
providers := make(moduledeps.Providers, len(providerConfigs))
|
|
|
|
// Any providerConfigs elements are *explicit* provider dependencies,
|
|
// which is the only situation where the user might provide an actual
|
|
// version constraint. We'll take care of these first.
|
|
for fullName, pCfg := range providerConfigs {
|
|
inst := moduledeps.ProviderInstance(fullName)
|
|
versionSet := discovery.AllVersions
|
|
if pCfg.Version != "" {
|
|
versionSet = discovery.ConstraintStr(pCfg.Version).MustParse()
|
|
}
|
|
providers[inst] = moduledeps.ProviderDependency{
|
|
Constraints: versionSet,
|
|
Reason: moduledeps.ProviderDependencyExplicit,
|
|
}
|
|
}
|
|
|
|
// Each resource in the configuration creates an *implicit* provider
|
|
// dependency, though we'll only record it if there isn't already
|
|
// an explicit dependency on the same provider.
|
|
for _, rc := range cfg.Resources {
|
|
fullName := rc.ProviderFullName()
|
|
inst := moduledeps.ProviderInstance(fullName)
|
|
if _, exists := providers[inst]; exists {
|
|
// Explicit dependency already present
|
|
continue
|
|
}
|
|
|
|
reason := moduledeps.ProviderDependencyImplicit
|
|
if _, inherited := inheritProviders[fullName]; inherited {
|
|
reason = moduledeps.ProviderDependencyInherited
|
|
}
|
|
|
|
providers[inst] = moduledeps.ProviderDependency{
|
|
Constraints: discovery.AllVersions,
|
|
Reason: reason,
|
|
}
|
|
}
|
|
|
|
ret.Providers = providers
|
|
}
|
|
|
|
childInherit := make(map[string]*config.ProviderConfig)
|
|
for k, v := range inheritProviders {
|
|
childInherit[k] = v
|
|
}
|
|
for k, v := range providerConfigs {
|
|
childInherit[k] = v
|
|
}
|
|
for _, c := range root.Children() {
|
|
ret.Children = append(ret.Children, moduleTreeConfigDependencies(c, childInherit))
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func moduleTreeMergeStateDependencies(root *moduledeps.Module, state *State) {
|
|
if state == nil {
|
|
return
|
|
}
|
|
|
|
findModule := func(path []string) *moduledeps.Module {
|
|
module := root
|
|
for _, name := range path[1:] { // skip initial "root"
|
|
var next *moduledeps.Module
|
|
for _, cm := range module.Children {
|
|
if cm.Name == name {
|
|
next = cm
|
|
break
|
|
}
|
|
}
|
|
|
|
if next == nil {
|
|
// If we didn't find a next node, we'll need to make one
|
|
next = &moduledeps.Module{
|
|
Name: name,
|
|
}
|
|
module.Children = append(module.Children, next)
|
|
}
|
|
|
|
module = next
|
|
}
|
|
return module
|
|
}
|
|
|
|
for _, ms := range state.Modules {
|
|
module := findModule(ms.Path)
|
|
|
|
for _, is := range ms.Resources {
|
|
fullName := config.ResourceProviderFullName(is.Type, is.Provider)
|
|
inst := moduledeps.ProviderInstance(fullName)
|
|
if _, exists := module.Providers[inst]; !exists {
|
|
if module.Providers == nil {
|
|
module.Providers = make(moduledeps.Providers)
|
|
}
|
|
module.Providers[inst] = moduledeps.ProviderDependency{
|
|
Constraints: discovery.AllVersions,
|
|
Reason: moduledeps.ProviderDependencyFromState,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|