mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
39e609d5fd
Previously we were using the experimental HCL 2 repository, but now we'll shift over to the v2 import path within the main HCL repository as part of actually releasing HCL 2.0 as stable. This is a mechanical search/replace to the new import paths. It also switches to the v2.0.0 release of HCL, which includes some new code that Terraform didn't previously have but should not change any behavior that matters for Terraform's purposes. For the moment the experimental HCL2 repository is still an indirect dependency via terraform-config-inspect, so it remains in our go.sum and vendor directories for the moment. Because terraform-config-inspect uses a much smaller subset of the HCL2 functionality, this does still manage to prune the vendor directory a little. A subsequent release of terraform-config-inspect should allow us to completely remove that old repository in a future commit.
298 lines
9.9 KiB
Go
298 lines
9.9 KiB
Go
package addrs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
)
|
|
|
|
// ProviderConfig is the address of a provider configuration.
|
|
type ProviderConfig struct {
|
|
Type string
|
|
|
|
// If not empty, Alias identifies which non-default (aliased) provider
|
|
// configuration this address refers to.
|
|
Alias string
|
|
}
|
|
|
|
// NewDefaultProviderConfig returns the address of the default (un-aliased)
|
|
// configuration for the provider with the given type name.
|
|
func NewDefaultProviderConfig(typeName string) ProviderConfig {
|
|
return ProviderConfig{
|
|
Type: typeName,
|
|
}
|
|
}
|
|
|
|
// ParseProviderConfigCompact parses the given absolute traversal as a relative
|
|
// provider address in compact form. The following are examples of traversals
|
|
// that can be successfully parsed as compact relative provider configuration
|
|
// addresses:
|
|
//
|
|
// aws
|
|
// aws.foo
|
|
//
|
|
// This function will panic if given a relative traversal.
|
|
//
|
|
// If the returned diagnostics contains errors then the result value is invalid
|
|
// and must not be used.
|
|
func ParseProviderConfigCompact(traversal hcl.Traversal) (ProviderConfig, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
ret := ProviderConfig{
|
|
Type: traversal.RootName(),
|
|
}
|
|
|
|
if len(traversal) < 2 {
|
|
// Just a type name, then.
|
|
return ret, diags
|
|
}
|
|
|
|
aliasStep := traversal[1]
|
|
switch ts := aliasStep.(type) {
|
|
case hcl.TraverseAttr:
|
|
ret.Alias = ts.Name
|
|
return ret, diags
|
|
default:
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider configuration address",
|
|
Detail: "The provider type name must either stand alone or be followed by an alias name separated with a dot.",
|
|
Subject: aliasStep.SourceRange().Ptr(),
|
|
})
|
|
}
|
|
|
|
if len(traversal) > 2 {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider configuration address",
|
|
Detail: "Extraneous extra operators after provider configuration address.",
|
|
Subject: traversal[2:].SourceRange().Ptr(),
|
|
})
|
|
}
|
|
|
|
return ret, diags
|
|
}
|
|
|
|
// ParseProviderConfigCompactStr is a helper wrapper around ParseProviderConfigCompact
|
|
// that takes a string and parses it with the HCL native syntax traversal parser
|
|
// before interpreting it.
|
|
//
|
|
// This should be used only in specialized situations since it will cause the
|
|
// created references to not have any meaningful source location information.
|
|
// If a reference string is coming from a source that should be identified in
|
|
// error messages then the caller should instead parse it directly using a
|
|
// suitable function from the HCL API and pass the traversal itself to
|
|
// ParseProviderConfigCompact.
|
|
//
|
|
// Error diagnostics are returned if either the parsing fails or the analysis
|
|
// of the traversal fails. There is no way for the caller to distinguish the
|
|
// two kinds of diagnostics programmatically. If error diagnostics are returned
|
|
// then the returned address is invalid.
|
|
func ParseProviderConfigCompactStr(str string) (ProviderConfig, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
|
|
diags = diags.Append(parseDiags)
|
|
if parseDiags.HasErrors() {
|
|
return ProviderConfig{}, diags
|
|
}
|
|
|
|
addr, addrDiags := ParseProviderConfigCompact(traversal)
|
|
diags = diags.Append(addrDiags)
|
|
return addr, diags
|
|
}
|
|
|
|
// Absolute returns an AbsProviderConfig from the receiver and the given module
|
|
// instance address.
|
|
func (pc ProviderConfig) Absolute(module ModuleInstance) AbsProviderConfig {
|
|
return AbsProviderConfig{
|
|
Module: module,
|
|
ProviderConfig: pc,
|
|
}
|
|
}
|
|
|
|
func (pc ProviderConfig) String() string {
|
|
if pc.Type == "" {
|
|
// Should never happen; always indicates a bug
|
|
return "provider.<invalid>"
|
|
}
|
|
|
|
if pc.Alias != "" {
|
|
return fmt.Sprintf("provider.%s.%s", pc.Type, pc.Alias)
|
|
}
|
|
|
|
return "provider." + pc.Type
|
|
}
|
|
|
|
// StringCompact is an alternative to String that returns the form that can
|
|
// be parsed by ParseProviderConfigCompact, without the "provider." prefix.
|
|
func (pc ProviderConfig) StringCompact() string {
|
|
if pc.Alias != "" {
|
|
return fmt.Sprintf("%s.%s", pc.Type, pc.Alias)
|
|
}
|
|
return pc.Type
|
|
}
|
|
|
|
// AbsProviderConfig is the absolute address of a provider configuration
|
|
// within a particular module instance.
|
|
type AbsProviderConfig struct {
|
|
Module ModuleInstance
|
|
ProviderConfig ProviderConfig
|
|
}
|
|
|
|
// ParseAbsProviderConfig parses the given traversal as an absolute provider
|
|
// address. The following are examples of traversals that can be successfully
|
|
// parsed as absolute provider configuration addresses:
|
|
//
|
|
// provider.aws
|
|
// provider.aws.foo
|
|
// module.bar.provider.aws
|
|
// module.bar.module.baz.provider.aws.foo
|
|
// module.foo[1].provider.aws.foo
|
|
//
|
|
// This type of address is used, for example, to record the relationships
|
|
// between resources and provider configurations in the state structure.
|
|
// This type of address is not generally used in the UI, except in error
|
|
// messages that refer to provider configurations.
|
|
func ParseAbsProviderConfig(traversal hcl.Traversal) (AbsProviderConfig, tfdiags.Diagnostics) {
|
|
modInst, remain, diags := parseModuleInstancePrefix(traversal)
|
|
ret := AbsProviderConfig{
|
|
Module: modInst,
|
|
}
|
|
if len(remain) < 2 || remain.RootName() != "provider" {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider configuration address",
|
|
Detail: "Provider address must begin with \"provider.\", followed by a provider type name.",
|
|
Subject: remain.SourceRange().Ptr(),
|
|
})
|
|
return ret, diags
|
|
}
|
|
if len(remain) > 3 {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider configuration address",
|
|
Detail: "Extraneous operators after provider configuration alias.",
|
|
Subject: hcl.Traversal(remain[3:]).SourceRange().Ptr(),
|
|
})
|
|
return ret, diags
|
|
}
|
|
|
|
if tt, ok := remain[1].(hcl.TraverseAttr); ok {
|
|
ret.ProviderConfig.Type = tt.Name
|
|
} else {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider configuration address",
|
|
Detail: "The prefix \"provider.\" must be followed by a provider type name.",
|
|
Subject: remain[1].SourceRange().Ptr(),
|
|
})
|
|
return ret, diags
|
|
}
|
|
|
|
if len(remain) == 3 {
|
|
if tt, ok := remain[2].(hcl.TraverseAttr); ok {
|
|
ret.ProviderConfig.Alias = tt.Name
|
|
} else {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider configuration address",
|
|
Detail: "Provider type name must be followed by a configuration alias name.",
|
|
Subject: remain[2].SourceRange().Ptr(),
|
|
})
|
|
return ret, diags
|
|
}
|
|
}
|
|
|
|
return ret, diags
|
|
}
|
|
|
|
// ParseAbsProviderConfigStr is a helper wrapper around ParseAbsProviderConfig
|
|
// that takes a string and parses it with the HCL native syntax traversal parser
|
|
// before interpreting it.
|
|
//
|
|
// This should be used only in specialized situations since it will cause the
|
|
// created references to not have any meaningful source location information.
|
|
// If a reference string is coming from a source that should be identified in
|
|
// error messages then the caller should instead parse it directly using a
|
|
// suitable function from the HCL API and pass the traversal itself to
|
|
// ParseAbsProviderConfig.
|
|
//
|
|
// Error diagnostics are returned if either the parsing fails or the analysis
|
|
// of the traversal fails. There is no way for the caller to distinguish the
|
|
// two kinds of diagnostics programmatically. If error diagnostics are returned
|
|
// the returned address is invalid.
|
|
func ParseAbsProviderConfigStr(str string) (AbsProviderConfig, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
|
|
diags = diags.Append(parseDiags)
|
|
if parseDiags.HasErrors() {
|
|
return AbsProviderConfig{}, diags
|
|
}
|
|
|
|
addr, addrDiags := ParseAbsProviderConfig(traversal)
|
|
diags = diags.Append(addrDiags)
|
|
return addr, diags
|
|
}
|
|
|
|
// ProviderConfigDefault returns the address of the default provider config
|
|
// of the given type inside the recieving module instance.
|
|
func (m ModuleInstance) ProviderConfigDefault(name string) AbsProviderConfig {
|
|
return AbsProviderConfig{
|
|
Module: m,
|
|
ProviderConfig: ProviderConfig{
|
|
Type: name,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ProviderConfigAliased returns the address of an aliased provider config
|
|
// of with given type and alias inside the recieving module instance.
|
|
func (m ModuleInstance) ProviderConfigAliased(name, alias string) AbsProviderConfig {
|
|
return AbsProviderConfig{
|
|
Module: m,
|
|
ProviderConfig: ProviderConfig{
|
|
Type: name,
|
|
Alias: alias,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Inherited returns an address that the receiving configuration address might
|
|
// inherit from in a parent module. The second bool return value indicates if
|
|
// such inheritance is possible, and thus whether the returned address is valid.
|
|
//
|
|
// Inheritance is possible only for default (un-aliased) providers in modules
|
|
// other than the root module. Even if a valid address is returned, inheritence
|
|
// may not be performed for other reasons, such as if the calling module
|
|
// provided explicit provider configurations within the call for this module.
|
|
// The ProviderTransformer graph transform in the main terraform module has
|
|
// the authoritative logic for provider inheritance, and this method is here
|
|
// mainly just for its benefit.
|
|
func (pc AbsProviderConfig) Inherited() (AbsProviderConfig, bool) {
|
|
// Can't inherit if we're already in the root.
|
|
if len(pc.Module) == 0 {
|
|
return AbsProviderConfig{}, false
|
|
}
|
|
|
|
// Can't inherit if we have an alias.
|
|
if pc.ProviderConfig.Alias != "" {
|
|
return AbsProviderConfig{}, false
|
|
}
|
|
|
|
// Otherwise, we might inherit from a configuration with the same
|
|
// provider name in the parent module instance.
|
|
parentMod := pc.Module.Parent()
|
|
return pc.ProviderConfig.Absolute(parentMod), true
|
|
}
|
|
|
|
func (pc AbsProviderConfig) String() string {
|
|
if len(pc.Module) == 0 {
|
|
return pc.ProviderConfig.String()
|
|
}
|
|
return fmt.Sprintf("%s.%s", pc.Module.String(), pc.ProviderConfig.String())
|
|
}
|