]>
Commit | Line | Data |
---|---|---|
c680a8e1 RS |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "github.com/hashicorp/terraform/config" | |
5 | "github.com/hashicorp/terraform/config/module" | |
6 | "github.com/hashicorp/terraform/moduledeps" | |
7 | "github.com/hashicorp/terraform/plugin/discovery" | |
8 | ) | |
9 | ||
10 | // ModuleTreeDependencies returns the dependencies of the tree of modules | |
11 | // described by the given configuration tree and state. | |
12 | // | |
13 | // Both configuration and state are required because there can be resources | |
14 | // implied by instances in the state that no longer exist in config. | |
15 | // | |
16 | // This function will panic if any invalid version constraint strings are | |
17 | // present in the configuration. This is guaranteed not to happen for any | |
18 | // configuration that has passed a call to Config.Validate(). | |
19 | func ModuleTreeDependencies(root *module.Tree, state *State) *moduledeps.Module { | |
20 | ||
21 | // First we walk the configuration tree to build the overall structure | |
22 | // and capture the explicit/implicit/inherited provider dependencies. | |
23 | deps := moduleTreeConfigDependencies(root, nil) | |
24 | ||
25 | // Next we walk over the resources in the state to catch any additional | |
26 | // dependencies created by existing resources that are no longer in config. | |
27 | // Most things we find in state will already be present in 'deps', but | |
28 | // we're interested in the rare thing that isn't. | |
29 | moduleTreeMergeStateDependencies(deps, state) | |
30 | ||
31 | return deps | |
32 | } | |
33 | ||
34 | func moduleTreeConfigDependencies(root *module.Tree, inheritProviders map[string]*config.ProviderConfig) *moduledeps.Module { | |
35 | if root == nil { | |
36 | // If no config is provided, we'll make a synthetic root. | |
37 | // This isn't necessarily correct if we're called with a nil that | |
38 | // *isn't* at the root, but in practice that can never happen. | |
39 | return &moduledeps.Module{ | |
40 | Name: "root", | |
41 | } | |
42 | } | |
43 | ||
44 | ret := &moduledeps.Module{ | |
45 | Name: root.Name(), | |
46 | } | |
47 | ||
48 | cfg := root.Config() | |
49 | providerConfigs := cfg.ProviderConfigsByFullName() | |
50 | ||
51 | // Provider dependencies | |
52 | { | |
53 | providers := make(moduledeps.Providers, len(providerConfigs)) | |
54 | ||
55 | // Any providerConfigs elements are *explicit* provider dependencies, | |
56 | // which is the only situation where the user might provide an actual | |
57 | // version constraint. We'll take care of these first. | |
58 | for fullName, pCfg := range providerConfigs { | |
59 | inst := moduledeps.ProviderInstance(fullName) | |
60 | versionSet := discovery.AllVersions | |
61 | if pCfg.Version != "" { | |
62 | versionSet = discovery.ConstraintStr(pCfg.Version).MustParse() | |
63 | } | |
64 | providers[inst] = moduledeps.ProviderDependency{ | |
65 | Constraints: versionSet, | |
66 | Reason: moduledeps.ProviderDependencyExplicit, | |
67 | } | |
68 | } | |
69 | ||
70 | // Each resource in the configuration creates an *implicit* provider | |
71 | // dependency, though we'll only record it if there isn't already | |
72 | // an explicit dependency on the same provider. | |
73 | for _, rc := range cfg.Resources { | |
74 | fullName := rc.ProviderFullName() | |
75 | inst := moduledeps.ProviderInstance(fullName) | |
76 | if _, exists := providers[inst]; exists { | |
77 | // Explicit dependency already present | |
78 | continue | |
79 | } | |
80 | ||
81 | reason := moduledeps.ProviderDependencyImplicit | |
82 | if _, inherited := inheritProviders[fullName]; inherited { | |
83 | reason = moduledeps.ProviderDependencyInherited | |
84 | } | |
85 | ||
86 | providers[inst] = moduledeps.ProviderDependency{ | |
87 | Constraints: discovery.AllVersions, | |
88 | Reason: reason, | |
89 | } | |
90 | } | |
91 | ||
92 | ret.Providers = providers | |
93 | } | |
94 | ||
95 | childInherit := make(map[string]*config.ProviderConfig) | |
96 | for k, v := range inheritProviders { | |
97 | childInherit[k] = v | |
98 | } | |
99 | for k, v := range providerConfigs { | |
100 | childInherit[k] = v | |
101 | } | |
102 | for _, c := range root.Children() { | |
103 | ret.Children = append(ret.Children, moduleTreeConfigDependencies(c, childInherit)) | |
104 | } | |
105 | ||
106 | return ret | |
107 | } | |
108 | ||
109 | func moduleTreeMergeStateDependencies(root *moduledeps.Module, state *State) { | |
110 | if state == nil { | |
111 | return | |
112 | } | |
113 | ||
114 | findModule := func(path []string) *moduledeps.Module { | |
115 | module := root | |
116 | for _, name := range path[1:] { // skip initial "root" | |
117 | var next *moduledeps.Module | |
118 | for _, cm := range module.Children { | |
119 | if cm.Name == name { | |
120 | next = cm | |
121 | break | |
122 | } | |
123 | } | |
124 | ||
125 | if next == nil { | |
126 | // If we didn't find a next node, we'll need to make one | |
127 | next = &moduledeps.Module{ | |
128 | Name: name, | |
129 | } | |
130 | module.Children = append(module.Children, next) | |
131 | } | |
132 | ||
133 | module = next | |
134 | } | |
135 | return module | |
136 | } | |
137 | ||
138 | for _, ms := range state.Modules { | |
139 | module := findModule(ms.Path) | |
140 | ||
141 | for _, is := range ms.Resources { | |
142 | fullName := config.ResourceProviderFullName(is.Type, is.Provider) | |
143 | inst := moduledeps.ProviderInstance(fullName) | |
144 | if _, exists := module.Providers[inst]; !exists { | |
145 | if module.Providers == nil { | |
146 | module.Providers = make(moduledeps.Providers) | |
147 | } | |
148 | module.Providers[inst] = moduledeps.ProviderDependency{ | |
149 | Constraints: discovery.AllVersions, | |
150 | Reason: moduledeps.ProviderDependencyFromState, | |
151 | } | |
152 | } | |
153 | } | |
154 | } | |
155 | ||
156 | } |