]>
Commit | Line | Data |
---|---|---|
c680a8e1 RS |
1 | package terraform |
2 | ||
3 | import ( | |
107c1cdb ND |
4 | version "github.com/hashicorp/go-version" |
5 | ||
6 | "github.com/hashicorp/terraform/addrs" | |
7 | "github.com/hashicorp/terraform/configs" | |
c680a8e1 RS |
8 | "github.com/hashicorp/terraform/moduledeps" |
9 | "github.com/hashicorp/terraform/plugin/discovery" | |
107c1cdb | 10 | "github.com/hashicorp/terraform/states" |
c680a8e1 RS |
11 | ) |
12 | ||
107c1cdb ND |
13 | // ConfigTreeDependencies returns the dependencies of the tree of modules |
14 | // described by the given configuration and state. | |
c680a8e1 RS |
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. | |
107c1cdb | 18 | func ConfigTreeDependencies(root *configs.Config, state *states.State) *moduledeps.Module { |
c680a8e1 RS |
19 | // First we walk the configuration tree to build the overall structure |
20 | // and capture the explicit/implicit/inherited provider dependencies. | |
107c1cdb | 21 | deps := configTreeConfigDependencies(root, nil) |
c680a8e1 RS |
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. | |
107c1cdb | 27 | configTreeMergeStateDependencies(deps, state) |
c680a8e1 RS |
28 | |
29 | return deps | |
30 | } | |
31 | ||
107c1cdb | 32 | func configTreeConfigDependencies(root *configs.Config, inheritProviders map[string]*configs.Provider) *moduledeps.Module { |
c680a8e1 RS |
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{ | |
107c1cdb ND |
38 | Name: "root", |
39 | Providers: make(moduledeps.Providers), | |
c680a8e1 RS |
40 | } |
41 | } | |
42 | ||
107c1cdb ND |
43 | name := "root" |
44 | if len(root.Path) != 0 { | |
45 | name = root.Path[len(root.Path)-1] | |
46 | } | |
47 | ||
c680a8e1 | 48 | ret := &moduledeps.Module{ |
107c1cdb | 49 | Name: name, |
c680a8e1 RS |
50 | } |
51 | ||
107c1cdb | 52 | module := root.Module |
c680a8e1 RS |
53 | |
54 | // Provider dependencies | |
55 | { | |
107c1cdb | 56 | providers := make(moduledeps.Providers) |
c680a8e1 | 57 | |
107c1cdb ND |
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 { | |
c680a8e1 | 62 | inst := moduledeps.ProviderInstance(fullName) |
107c1cdb ND |
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...) | |
c680a8e1 | 74 | } |
107c1cdb ND |
75 | discoConstraints := discovery.NewConstraints(rawConstraints) |
76 | ||
c680a8e1 | 77 | providers[inst] = moduledeps.ProviderDependency{ |
107c1cdb | 78 | Constraints: discoConstraints, |
c680a8e1 RS |
79 | Reason: moduledeps.ProviderDependencyExplicit, |
80 | } | |
81 | } | |
82 | ||
107c1cdb ND |
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 | ||
c680a8e1 RS |
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. | |
107c1cdb ND |
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()) | |
c680a8e1 RS |
126 | if _, exists := providers[inst]; exists { |
127 | // Explicit dependency already present | |
128 | continue | |
129 | } | |
130 | ||
131 | reason := moduledeps.ProviderDependencyImplicit | |
107c1cdb | 132 | if _, inherited := inheritProviders[addr.String()]; inherited { |
c680a8e1 RS |
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 | ||
107c1cdb | 145 | childInherit := make(map[string]*configs.Provider) |
c680a8e1 RS |
146 | for k, v := range inheritProviders { |
147 | childInherit[k] = v | |
148 | } | |
107c1cdb | 149 | for k, v := range module.ProviderConfigs { |
c680a8e1 RS |
150 | childInherit[k] = v |
151 | } | |
107c1cdb ND |
152 | for _, c := range root.Children { |
153 | ret.Children = append(ret.Children, configTreeConfigDependencies(c, childInherit)) | |
c680a8e1 RS |
154 | } |
155 | ||
156 | return ret | |
157 | } | |
158 | ||
107c1cdb | 159 | func configTreeMergeStateDependencies(root *moduledeps.Module, state *states.State) { |
c680a8e1 RS |
160 | if state == nil { |
161 | return | |
162 | } | |
163 | ||
107c1cdb | 164 | findModule := func(path addrs.ModuleInstance) *moduledeps.Module { |
c680a8e1 | 165 | module := root |
107c1cdb | 166 | for _, step := range path { |
c680a8e1 RS |
167 | var next *moduledeps.Module |
168 | for _, cm := range module.Children { | |
107c1cdb | 169 | if cm.Name == step.Name { |
c680a8e1 RS |
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{ | |
107c1cdb ND |
178 | Name: step.Name, |
179 | Providers: make(moduledeps.Providers), | |
c680a8e1 RS |
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 { | |
107c1cdb | 190 | module := findModule(ms.Addr) |
c680a8e1 | 191 | |
107c1cdb ND |
192 | for _, rs := range ms.Resources { |
193 | inst := moduledeps.ProviderInstance(rs.ProviderConfig.ProviderConfig.StringCompact()) | |
c680a8e1 | 194 | if _, exists := module.Providers[inst]; !exists { |
c680a8e1 RS |
195 | module.Providers[inst] = moduledeps.ProviderDependency{ |
196 | Constraints: discovery.AllVersions, | |
197 | Reason: moduledeps.ProviderDependencyFromState, | |
198 | } | |
199 | } | |
200 | } | |
201 | } | |
c680a8e1 | 202 | } |