github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/configs/config.go (about) 1 package configs 2 3 import ( 4 "fmt" 5 "sort" 6 7 version "github.com/hashicorp/go-version" 8 "github.com/hashicorp/hcl/v2" 9 "github.com/iaas-resource-provision/iaas-rpc/internal/addrs" 10 "github.com/iaas-resource-provision/iaas-rpc/internal/getproviders" 11 ) 12 13 // A Config is a node in the tree of modules within a configuration. 14 // 15 // The module tree is constructed by following ModuleCall instances recursively 16 // through the root module transitively into descendent modules. 17 // 18 // A module tree described in *this* package represents the static tree 19 // represented by configuration. During evaluation a static ModuleNode may 20 // expand into zero or more module instances depending on the use of count and 21 // for_each configuration attributes within each call. 22 type Config struct { 23 // RootModule points to the Config for the root module within the same 24 // module tree as this module. If this module _is_ the root module then 25 // this is self-referential. 26 Root *Config 27 28 // ParentModule points to the Config for the module that directly calls 29 // this module. If this is the root module then this field is nil. 30 Parent *Config 31 32 // Path is a sequence of module logical names that traverse from the root 33 // module to this config. Path is empty for the root module. 34 // 35 // This should only be used to display paths to the end-user in rare cases 36 // where we are talking about the static module tree, before module calls 37 // have been resolved. In most cases, an addrs.ModuleInstance describing 38 // a node in the dynamic module tree is better, since it will then include 39 // any keys resulting from evaluating "count" and "for_each" arguments. 40 Path addrs.Module 41 42 // ChildModules points to the Config for each of the direct child modules 43 // called from this module. The keys in this map match the keys in 44 // Module.ModuleCalls. 45 Children map[string]*Config 46 47 // Module points to the object describing the configuration for the 48 // various elements (variables, resources, etc) defined by this module. 49 Module *Module 50 51 // CallRange is the source range for the header of the module block that 52 // requested this module. 53 // 54 // This field is meaningless for the root module, where its contents are undefined. 55 CallRange hcl.Range 56 57 // SourceAddr is the source address that the referenced module was requested 58 // from, as specified in configuration. 59 // 60 // This field is meaningless for the root module, where its contents are undefined. 61 SourceAddr string 62 63 // SourceAddrRange is the location in the configuration source where the 64 // SourceAddr value was set, for use in diagnostic messages. 65 // 66 // This field is meaningless for the root module, where its contents are undefined. 67 SourceAddrRange hcl.Range 68 69 // Version is the specific version that was selected for this module, 70 // based on version constraints given in configuration. 71 // 72 // This field is nil if the module was loaded from a non-registry source, 73 // since versions are not supported for other sources. 74 // 75 // This field is meaningless for the root module, where it will always 76 // be nil. 77 Version *version.Version 78 } 79 80 // ModuleRequirements represents the provider requirements for an individual 81 // module, along with references to any child modules. This is used to 82 // determine which modules require which providers. 83 type ModuleRequirements struct { 84 Name string 85 SourceAddr string 86 SourceDir string 87 Requirements getproviders.Requirements 88 Children map[string]*ModuleRequirements 89 } 90 91 // NewEmptyConfig constructs a single-node configuration tree with an empty 92 // root module. This is generally a pretty useless thing to do, so most callers 93 // should instead use BuildConfig. 94 func NewEmptyConfig() *Config { 95 ret := &Config{} 96 ret.Root = ret 97 ret.Children = make(map[string]*Config) 98 ret.Module = &Module{} 99 return ret 100 } 101 102 // Depth returns the number of "hops" the receiver is from the root of its 103 // module tree, with the root module having a depth of zero. 104 func (c *Config) Depth() int { 105 ret := 0 106 this := c 107 for this.Parent != nil { 108 ret++ 109 this = this.Parent 110 } 111 return ret 112 } 113 114 // DeepEach calls the given function once for each module in the tree, starting 115 // with the receiver. 116 // 117 // A parent is always called before its children and children of a particular 118 // node are visited in lexicographic order by their names. 119 func (c *Config) DeepEach(cb func(c *Config)) { 120 cb(c) 121 122 names := make([]string, 0, len(c.Children)) 123 for name := range c.Children { 124 names = append(names, name) 125 } 126 127 for _, name := range names { 128 c.Children[name].DeepEach(cb) 129 } 130 } 131 132 // AllModules returns a slice of all the receiver and all of its descendent 133 // nodes in the module tree, in the same order they would be visited by 134 // DeepEach. 135 func (c *Config) AllModules() []*Config { 136 var ret []*Config 137 c.DeepEach(func(c *Config) { 138 ret = append(ret, c) 139 }) 140 return ret 141 } 142 143 // Descendent returns the descendent config that has the given path beneath 144 // the receiver, or nil if there is no such module. 145 // 146 // The path traverses the static module tree, prior to any expansion to handle 147 // count and for_each arguments. 148 // 149 // An empty path will just return the receiver, and is therefore pointless. 150 func (c *Config) Descendent(path addrs.Module) *Config { 151 current := c 152 for _, name := range path { 153 current = current.Children[name] 154 if current == nil { 155 return nil 156 } 157 } 158 return current 159 } 160 161 // DescendentForInstance is like Descendent except that it accepts a path 162 // to a particular module instance in the dynamic module graph, returning 163 // the node from the static module graph that corresponds to it. 164 // 165 // All instances created by a particular module call share the same 166 // configuration, so the keys within the given path are disregarded. 167 func (c *Config) DescendentForInstance(path addrs.ModuleInstance) *Config { 168 current := c 169 for _, step := range path { 170 current = current.Children[step.Name] 171 if current == nil { 172 return nil 173 } 174 } 175 return current 176 } 177 178 // ProviderRequirements searches the full tree of modules under the receiver 179 // for both explicit and implicit dependencies on providers. 180 // 181 // The result is a full manifest of all of the providers that must be available 182 // in order to work with the receiving configuration. 183 // 184 // If the returned diagnostics includes errors then the resulting Requirements 185 // may be incomplete. 186 func (c *Config) ProviderRequirements() (getproviders.Requirements, hcl.Diagnostics) { 187 reqs := make(getproviders.Requirements) 188 diags := c.addProviderRequirements(reqs, true) 189 190 return reqs, diags 191 } 192 193 // ProviderRequirementsShallow searches only the direct receiver for explicit 194 // and implicit dependencies on providers. Descendant modules are ignored. 195 // 196 // If the returned diagnostics includes errors then the resulting Requirements 197 // may be incomplete. 198 func (c *Config) ProviderRequirementsShallow() (getproviders.Requirements, hcl.Diagnostics) { 199 reqs := make(getproviders.Requirements) 200 diags := c.addProviderRequirements(reqs, false) 201 202 return reqs, diags 203 } 204 205 // ProviderRequirementsByModule searches the full tree of modules under the 206 // receiver for both explicit and implicit dependencies on providers, 207 // constructing a tree where the requirements are broken out by module. 208 // 209 // If the returned diagnostics includes errors then the resulting Requirements 210 // may be incomplete. 211 func (c *Config) ProviderRequirementsByModule() (*ModuleRequirements, hcl.Diagnostics) { 212 reqs := make(getproviders.Requirements) 213 diags := c.addProviderRequirements(reqs, false) 214 215 children := make(map[string]*ModuleRequirements) 216 for name, child := range c.Children { 217 childReqs, childDiags := child.ProviderRequirementsByModule() 218 childReqs.Name = name 219 children[name] = childReqs 220 diags = append(diags, childDiags...) 221 } 222 223 ret := &ModuleRequirements{ 224 SourceAddr: c.SourceAddr, 225 SourceDir: c.Module.SourceDir, 226 Requirements: reqs, 227 Children: children, 228 } 229 230 return ret, diags 231 } 232 233 // addProviderRequirements is the main part of the ProviderRequirements 234 // implementation, gradually mutating a shared requirements object to 235 // eventually return. If the recurse argument is true, the requirements will 236 // include all descendant modules; otherwise, only the specified module. 237 func (c *Config) addProviderRequirements(reqs getproviders.Requirements, recurse bool) hcl.Diagnostics { 238 var diags hcl.Diagnostics 239 240 // First we'll deal with the requirements directly in _our_ module... 241 if c.Module.ProviderRequirements != nil { 242 for _, providerReqs := range c.Module.ProviderRequirements.RequiredProviders { 243 fqn := providerReqs.Type 244 if _, ok := reqs[fqn]; !ok { 245 // We'll at least have an unconstrained dependency then, but might 246 // add to this in the loop below. 247 reqs[fqn] = nil 248 } 249 // The model of version constraints in this package is still the 250 // old one using a different upstream module to represent versions, 251 // so we'll need to shim that out here for now. The two parsers 252 // don't exactly agree in practice 🙄 so this might produce new errors. 253 // TODO: Use the new parser throughout this package so we can get the 254 // better error messages it produces in more situations. 255 constraints, err := getproviders.ParseVersionConstraints(providerReqs.Requirement.Required.String()) 256 if err != nil { 257 diags = diags.Append(&hcl.Diagnostic{ 258 Severity: hcl.DiagError, 259 Summary: "Invalid version constraint", 260 // The errors returned by ParseVersionConstraint already include 261 // the section of input that was incorrect, so we don't need to 262 // include that here. 263 Detail: fmt.Sprintf("Incorrect version constraint syntax: %s.", err.Error()), 264 Subject: providerReqs.Requirement.DeclRange.Ptr(), 265 }) 266 } 267 reqs[fqn] = append(reqs[fqn], constraints...) 268 } 269 } 270 271 // Each resource in the configuration creates an *implicit* provider 272 // dependency, though we'll only record it if there isn't already 273 // an explicit dependency on the same provider. 274 for _, rc := range c.Module.ManagedResources { 275 fqn := rc.Provider 276 if _, exists := reqs[fqn]; exists { 277 // Explicit dependency already present 278 continue 279 } 280 reqs[fqn] = nil 281 } 282 for _, rc := range c.Module.DataResources { 283 fqn := rc.Provider 284 if _, exists := reqs[fqn]; exists { 285 // Explicit dependency already present 286 continue 287 } 288 reqs[fqn] = nil 289 } 290 291 // "provider" block can also contain version constraints 292 for _, provider := range c.Module.ProviderConfigs { 293 fqn := c.Module.ProviderForLocalConfig(addrs.LocalProviderConfig{LocalName: provider.Name}) 294 if _, ok := reqs[fqn]; !ok { 295 // We'll at least have an unconstrained dependency then, but might 296 // add to this in the loop below. 297 reqs[fqn] = nil 298 } 299 if provider.Version.Required != nil { 300 // The model of version constraints in this package is still the 301 // old one using a different upstream module to represent versions, 302 // so we'll need to shim that out here for now. The two parsers 303 // don't exactly agree in practice 🙄 so this might produce new errors. 304 // TODO: Use the new parser throughout this package so we can get the 305 // better error messages it produces in more situations. 306 constraints, err := getproviders.ParseVersionConstraints(provider.Version.Required.String()) 307 if err != nil { 308 diags = diags.Append(&hcl.Diagnostic{ 309 Severity: hcl.DiagError, 310 Summary: "Invalid version constraint", 311 // The errors returned by ParseVersionConstraint already include 312 // the section of input that was incorrect, so we don't need to 313 // include that here. 314 Detail: fmt.Sprintf("Incorrect version constraint syntax: %s.", err.Error()), 315 Subject: provider.Version.DeclRange.Ptr(), 316 }) 317 } 318 reqs[fqn] = append(reqs[fqn], constraints...) 319 } 320 } 321 322 if recurse { 323 for _, childConfig := range c.Children { 324 moreDiags := childConfig.addProviderRequirements(reqs, true) 325 diags = append(diags, moreDiags...) 326 } 327 } 328 329 return diags 330 } 331 332 // resolveProviderTypes walks through the providers in the module and ensures 333 // the true types are assigned based on the provider requirements for the 334 // module. 335 func (c *Config) resolveProviderTypes() { 336 for _, child := range c.Children { 337 child.resolveProviderTypes() 338 } 339 340 // collect the required_providers, and then add any missing default providers 341 providers := map[string]addrs.Provider{} 342 for name, p := range c.Module.ProviderRequirements.RequiredProviders { 343 providers[name] = p.Type 344 } 345 346 // ensure all provider configs know their correct type 347 for _, p := range c.Module.ProviderConfigs { 348 addr, required := providers[p.Name] 349 if required { 350 p.providerType = addr 351 } else { 352 addr := addrs.NewDefaultProvider(p.Name) 353 p.providerType = addr 354 providers[p.Name] = addr 355 } 356 } 357 358 // connect module call providers to the correct type 359 for _, mod := range c.Module.ModuleCalls { 360 for _, p := range mod.Providers { 361 if addr, known := providers[p.InParent.Name]; known { 362 p.InParent.providerType = addr 363 } 364 } 365 } 366 367 // fill in parent module calls too 368 if c.Parent != nil { 369 for _, mod := range c.Parent.Module.ModuleCalls { 370 for _, p := range mod.Providers { 371 if addr, known := providers[p.InChild.Name]; known { 372 p.InChild.providerType = addr 373 } 374 } 375 } 376 } 377 } 378 379 // ProviderTypes returns the FQNs of each distinct provider type referenced 380 // in the receiving configuration. 381 // 382 // This is a helper for easily determining which provider types are required 383 // to fully interpret the configuration, though it does not include version 384 // information and so callers are expected to have already dealt with 385 // provider version selection in an earlier step and have identified suitable 386 // versions for each provider. 387 func (c *Config) ProviderTypes() []addrs.Provider { 388 // Ignore diagnostics here because they relate to version constraints 389 reqs, _ := c.ProviderRequirements() 390 391 ret := make([]addrs.Provider, 0, len(reqs)) 392 for k := range reqs { 393 ret = append(ret, k) 394 } 395 sort.Slice(ret, func(i, j int) bool { 396 return ret[i].String() < ret[j].String() 397 }) 398 return ret 399 } 400 401 // ResolveAbsProviderAddr returns the AbsProviderConfig represented by the given 402 // ProviderConfig address, which must not be nil or this method will panic. 403 // 404 // If the given address is already an AbsProviderConfig then this method returns 405 // it verbatim, and will always succeed. If it's a LocalProviderConfig then 406 // it will consult the local-to-FQN mapping table for the given module 407 // to find the absolute address corresponding to the given local one. 408 // 409 // The module address to resolve local addresses in must be given in the second 410 // argument, and must refer to a module that exists under the receiver or 411 // else this method will panic. 412 func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addrs.Module) addrs.AbsProviderConfig { 413 switch addr := addr.(type) { 414 415 case addrs.AbsProviderConfig: 416 return addr 417 418 case addrs.LocalProviderConfig: 419 // Find the descendent Config that contains the module that this 420 // local config belongs to. 421 mc := c.Descendent(inModule) 422 if mc == nil { 423 panic(fmt.Sprintf("ResolveAbsProviderAddr with non-existent module %s", inModule.String())) 424 } 425 426 var provider addrs.Provider 427 if providerReq, exists := c.Module.ProviderRequirements.RequiredProviders[addr.LocalName]; exists { 428 provider = providerReq.Type 429 } else { 430 provider = addrs.ImpliedProviderForUnqualifiedType(addr.LocalName) 431 } 432 433 return addrs.AbsProviderConfig{ 434 Module: inModule, 435 Provider: provider, 436 Alias: addr.Alias, 437 } 438 439 default: 440 panic(fmt.Sprintf("cannot ResolveAbsProviderAddr(%v, ...)", addr)) 441 } 442 443 } 444 445 // ProviderForConfigAddr returns the FQN for a given addrs.ProviderConfig, first 446 // by checking for the provider in module.ProviderRequirements and falling 447 // back to addrs.NewDefaultProvider if it is not found. 448 func (c *Config) ProviderForConfigAddr(addr addrs.LocalProviderConfig) addrs.Provider { 449 if provider, exists := c.Module.ProviderRequirements.RequiredProviders[addr.LocalName]; exists { 450 return provider.Type 451 } 452 return c.ResolveAbsProviderAddr(addr, addrs.RootModule).Provider 453 }