4 version "github.com/hashicorp/go-version"
6 "github.com/hashicorp/terraform/addrs"
7 "github.com/hashicorp/terraform/configs"
8 "github.com/hashicorp/terraform/moduledeps"
9 "github.com/hashicorp/terraform/plugin/discovery"
10 "github.com/hashicorp/terraform/states"
13 // ConfigTreeDependencies returns the dependencies of the tree of modules
14 // described by the given configuration and state.
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)
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)
32 func configTreeConfigDependencies(root *configs.Config, inheritProviders map[string]*configs.Provider) *moduledeps.Module {
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{
39 Providers: make(moduledeps.Providers),
44 if len(root.Path) != 0 {
45 name = root.Path[len(root.Path)-1]
48 ret := &moduledeps.Module{
54 // Provider dependencies
56 providers := make(moduledeps.Providers)
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)
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...)
75 discoConstraints := discovery.NewConstraints(rawConstraints)
77 providers[inst] = moduledeps.ProviderDependency{
78 Constraints: discoConstraints,
79 Reason: moduledeps.ProviderDependencyExplicit,
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)
92 if existing, exists := providers[inst]; exists {
93 existing.Constraints = existing.Constraints.Append(discoConstraints)
95 providers[inst] = moduledeps.ProviderDependency{
96 Constraints: discoConstraints,
97 Reason: moduledeps.ProviderDependencyExplicit,
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
113 reason := moduledeps.ProviderDependencyImplicit
114 if _, inherited := inheritProviders[addr.StringCompact()]; inherited {
115 reason = moduledeps.ProviderDependencyInherited
118 providers[inst] = moduledeps.ProviderDependency{
119 Constraints: discovery.AllVersions,
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
131 reason := moduledeps.ProviderDependencyImplicit
132 if _, inherited := inheritProviders[addr.String()]; inherited {
133 reason = moduledeps.ProviderDependencyInherited
136 providers[inst] = moduledeps.ProviderDependency{
137 Constraints: discovery.AllVersions,
142 ret.Providers = providers
145 childInherit := make(map[string]*configs.Provider)
146 for k, v := range inheritProviders {
149 for k, v := range module.ProviderConfigs {
152 for _, c := range root.Children {
153 ret.Children = append(ret.Children, configTreeConfigDependencies(c, childInherit))
159 func configTreeMergeStateDependencies(root *moduledeps.Module, state *states.State) {
164 findModule := func(path addrs.ModuleInstance) *moduledeps.Module {
166 for _, step := range path {
167 var next *moduledeps.Module
168 for _, cm := range module.Children {
169 if cm.Name == step.Name {
176 // If we didn't find a next node, we'll need to make one
177 next = &moduledeps.Module{
179 Providers: make(moduledeps.Providers),
181 module.Children = append(module.Children, next)
189 for _, ms := range state.Modules {
190 module := findModule(ms.Addr)
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,