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

     1  package terraform
     2  
     3  import (
     4  	version "github.com/hashicorp/go-version"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps"
     9  	"github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery"
    10  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
    11  )
    12  
    13  // ConfigTreeDependencies returns the dependencies of the tree of modules
    14  // described by the given configuration and state.
    15  //
    16  // Both configuration and state are required because there can be resources
    17  // implied by instances in the state that no longer exist in config.
    18  func ConfigTreeDependencies(root *configs.Config, state *states.State) *moduledeps.Module {
    19  	// First we walk the configuration tree to build the overall structure
    20  	// and capture the explicit/implicit/inherited provider dependencies.
    21  	deps := configTreeConfigDependencies(root, nil)
    22  
    23  	// Next we walk over the resources in the state to catch any additional
    24  	// dependencies created by existing resources that are no longer in config.
    25  	// Most things we find in state will already be present in 'deps', but
    26  	// we're interested in the rare thing that isn't.
    27  	configTreeMergeStateDependencies(deps, state)
    28  
    29  	return deps
    30  }
    31  
    32  func configTreeConfigDependencies(root *configs.Config, inheritProviders map[string]*configs.Provider) *moduledeps.Module {
    33  	if root == nil {
    34  		// If no config is provided, we'll make a synthetic root.
    35  		// This isn't necessarily correct if we're called with a nil that
    36  		// *isn't* at the root, but in practice that can never happen.
    37  		return &moduledeps.Module{
    38  			Name:      "root",
    39  			Providers: make(moduledeps.Providers),
    40  		}
    41  	}
    42  
    43  	name := "root"
    44  	if len(root.Path) != 0 {
    45  		name = root.Path[len(root.Path)-1]
    46  	}
    47  
    48  	ret := &moduledeps.Module{
    49  		Name: name,
    50  	}
    51  
    52  	module := root.Module
    53  
    54  	// Provider dependencies
    55  	{
    56  		providers := make(moduledeps.Providers)
    57  
    58  		// The main way to declare a provider dependency is explicitly inside
    59  		// the "terraform" block, which allows declaring a requirement without
    60  		// also creating a configuration.
    61  		for fullName, constraints := range module.ProviderRequirements {
    62  			inst := moduledeps.ProviderInstance(fullName)
    63  
    64  			// The handling here is a bit fiddly because the moduledeps package
    65  			// was designed around the legacy (pre-0.12) configuration model
    66  			// and hasn't yet been revised to handle the new model. As a result,
    67  			// we need to do some translation here.
    68  			// FIXME: Eventually we should adjust the underlying model so we
    69  			// can also retain the source location of each constraint, for
    70  			// more informative output from the "terraform providers" command.
    71  			var rawConstraints version.Constraints
    72  			for _, constraint := range constraints {
    73  				rawConstraints = append(rawConstraints, constraint.Required...)
    74  			}
    75  			discoConstraints := discovery.NewConstraints(rawConstraints)
    76  
    77  			providers[inst] = moduledeps.ProviderDependency{
    78  				Constraints: discoConstraints,
    79  				Reason:      moduledeps.ProviderDependencyExplicit,
    80  			}
    81  		}
    82  
    83  		// Provider configurations can also include version constraints,
    84  		// allowing for more terse declaration in situations where both a
    85  		// configuration and a constraint are defined in the same module.
    86  		for fullName, pCfg := range module.ProviderConfigs {
    87  			inst := moduledeps.ProviderInstance(fullName)
    88  			discoConstraints := discovery.AllVersions
    89  			if pCfg.Version.Required != nil {
    90  				discoConstraints = discovery.NewConstraints(pCfg.Version.Required)
    91  			}
    92  			if existing, exists := providers[inst]; exists {
    93  				existing.Constraints = existing.Constraints.Append(discoConstraints)
    94  			} else {
    95  				providers[inst] = moduledeps.ProviderDependency{
    96  					Constraints: discoConstraints,
    97  					Reason:      moduledeps.ProviderDependencyExplicit,
    98  				}
    99  			}
   100  		}
   101  
   102  		// Each resource in the configuration creates an *implicit* provider
   103  		// dependency, though we'll only record it if there isn't already
   104  		// an explicit dependency on the same provider.
   105  		for _, rc := range module.ManagedResources {
   106  			addr := rc.ProviderConfigAddr()
   107  			inst := moduledeps.ProviderInstance(addr.StringCompact())
   108  			if _, exists := providers[inst]; exists {
   109  				// Explicit dependency already present
   110  				continue
   111  			}
   112  
   113  			reason := moduledeps.ProviderDependencyImplicit
   114  			if _, inherited := inheritProviders[addr.StringCompact()]; inherited {
   115  				reason = moduledeps.ProviderDependencyInherited
   116  			}
   117  
   118  			providers[inst] = moduledeps.ProviderDependency{
   119  				Constraints: discovery.AllVersions,
   120  				Reason:      reason,
   121  			}
   122  		}
   123  		for _, rc := range module.DataResources {
   124  			addr := rc.ProviderConfigAddr()
   125  			inst := moduledeps.ProviderInstance(addr.StringCompact())
   126  			if _, exists := providers[inst]; exists {
   127  				// Explicit dependency already present
   128  				continue
   129  			}
   130  
   131  			reason := moduledeps.ProviderDependencyImplicit
   132  			if _, inherited := inheritProviders[addr.String()]; inherited {
   133  				reason = moduledeps.ProviderDependencyInherited
   134  			}
   135  
   136  			providers[inst] = moduledeps.ProviderDependency{
   137  				Constraints: discovery.AllVersions,
   138  				Reason:      reason,
   139  			}
   140  		}
   141  
   142  		ret.Providers = providers
   143  	}
   144  
   145  	childInherit := make(map[string]*configs.Provider)
   146  	for k, v := range inheritProviders {
   147  		childInherit[k] = v
   148  	}
   149  	for k, v := range module.ProviderConfigs {
   150  		childInherit[k] = v
   151  	}
   152  	for _, c := range root.Children {
   153  		ret.Children = append(ret.Children, configTreeConfigDependencies(c, childInherit))
   154  	}
   155  
   156  	return ret
   157  }
   158  
   159  func configTreeMergeStateDependencies(root *moduledeps.Module, state *states.State) {
   160  	if state == nil {
   161  		return
   162  	}
   163  
   164  	findModule := func(path addrs.ModuleInstance) *moduledeps.Module {
   165  		module := root
   166  		for _, step := range path {
   167  			var next *moduledeps.Module
   168  			for _, cm := range module.Children {
   169  				if cm.Name == step.Name {
   170  					next = cm
   171  					break
   172  				}
   173  			}
   174  
   175  			if next == nil {
   176  				// If we didn't find a next node, we'll need to make one
   177  				next = &moduledeps.Module{
   178  					Name:      step.Name,
   179  					Providers: make(moduledeps.Providers),
   180  				}
   181  				module.Children = append(module.Children, next)
   182  			}
   183  
   184  			module = next
   185  		}
   186  		return module
   187  	}
   188  
   189  	for _, ms := range state.Modules {
   190  		module := findModule(ms.Addr)
   191  
   192  		for _, rs := range ms.Resources {
   193  			inst := moduledeps.ProviderInstance(rs.ProviderConfig.ProviderConfig.StringCompact())
   194  			if _, exists := module.Providers[inst]; !exists {
   195  				module.Providers[inst] = moduledeps.ProviderDependency{
   196  					Constraints: discovery.AllVersions,
   197  					Reason:      moduledeps.ProviderDependencyFromState,
   198  				}
   199  			}
   200  		}
   201  	}
   202  }