aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go
blob: b9f44a0e869f175860c8f9dbf2bf6b358fd4631d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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,
				}
			}
		}
	}

}