298 lines
10 KiB
Go
298 lines
10 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 Provider
|
|
|
|
// 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: NewLegacyProvider(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: NewLegacyProvider(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.LegacyString() == "" {
|
|
// Should never happen; always indicates a bug
|
|
return "provider.<invalid>"
|
|
}
|
|
|
|
if pc.Alias != "" {
|
|
return fmt.Sprintf("provider.%s.%s", pc.Type.LegacyString(), pc.Alias)
|
|
}
|
|
|
|
return "provider." + pc.Type.LegacyString()
|
|
}
|
|
|
|
// 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.LegacyString(), pc.Alias)
|
|
}
|
|
return pc.Type.LegacyString()
|
|
}
|
|
|
|
// 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 = NewLegacyProvider(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: NewLegacyProvider(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: NewLegacyProvider(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())
|
|
}
|