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 }