210 lines
6.3 KiB
Go
210 lines
6.3 KiB
Go
package configs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/gohcl"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
// Provider represents a "provider" block in a module or file. A provider
|
|
// block is a provider configuration, and there can be zero or more
|
|
// configurations for each actual provider.
|
|
type Provider struct {
|
|
Name string
|
|
NameRange hcl.Range
|
|
Alias string
|
|
AliasRange *hcl.Range // nil if no alias set
|
|
|
|
Version VersionConstraint
|
|
|
|
Config hcl.Body
|
|
|
|
DeclRange hcl.Range
|
|
}
|
|
|
|
func decodeProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) {
|
|
var diags hcl.Diagnostics
|
|
|
|
// Produce deprecation messages for any pre-0.12-style
|
|
// single-interpolation-only expressions. We do this up front here because
|
|
// then we can also catch instances inside special blocks like "connection",
|
|
// before PartialContent extracts them.
|
|
moreDiags := warnForDeprecatedInterpolationsInBody(block.Body)
|
|
diags = append(diags, moreDiags...)
|
|
|
|
content, config, moreDiags := block.Body.PartialContent(providerBlockSchema)
|
|
diags = append(diags, moreDiags...)
|
|
|
|
provider := &Provider{
|
|
Name: block.Labels[0],
|
|
NameRange: block.LabelRanges[0],
|
|
Config: config,
|
|
DeclRange: block.DefRange,
|
|
}
|
|
|
|
if attr, exists := content.Attributes["alias"]; exists {
|
|
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &provider.Alias)
|
|
diags = append(diags, valDiags...)
|
|
provider.AliasRange = attr.Expr.Range().Ptr()
|
|
|
|
if !hclsyntax.ValidIdentifier(provider.Alias) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider configuration alias",
|
|
Detail: fmt.Sprintf("An alias must be a valid name. %s", badIdentifierDetail),
|
|
})
|
|
}
|
|
}
|
|
|
|
if attr, exists := content.Attributes["version"]; exists {
|
|
var versionDiags hcl.Diagnostics
|
|
provider.Version, versionDiags = decodeVersionConstraint(attr)
|
|
diags = append(diags, versionDiags...)
|
|
}
|
|
|
|
// Reserved attribute names
|
|
for _, name := range []string{"count", "depends_on", "for_each", "source"} {
|
|
if attr, exists := content.Attributes[name]; exists {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Reserved argument name in provider block",
|
|
Detail: fmt.Sprintf("The provider argument name %q is reserved for use by Terraform in a future version.", name),
|
|
Subject: &attr.NameRange,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Reserved block types (all of them)
|
|
for _, block := range content.Blocks {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Reserved block type name in provider block",
|
|
Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type),
|
|
Subject: &block.TypeRange,
|
|
})
|
|
}
|
|
|
|
return provider, diags
|
|
}
|
|
|
|
// Addr returns the address of the receiving provider configuration, relative
|
|
// to its containing module.
|
|
func (p *Provider) Addr() addrs.LocalProviderConfig {
|
|
return addrs.LocalProviderConfig{
|
|
LocalName: p.Name,
|
|
Alias: p.Alias,
|
|
}
|
|
}
|
|
|
|
func (p *Provider) moduleUniqueKey() string {
|
|
if p.Alias != "" {
|
|
return fmt.Sprintf("%s.%s", p.Name, p.Alias)
|
|
}
|
|
return p.Name
|
|
}
|
|
|
|
// 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) (addrs.LocalProviderConfig, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
ret := addrs.LocalProviderConfig{
|
|
LocalName: 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) (addrs.LocalProviderConfig, 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 addrs.LocalProviderConfig{}, diags
|
|
}
|
|
|
|
addr, addrDiags := ParseProviderConfigCompact(traversal)
|
|
diags = diags.Append(addrDiags)
|
|
return addr, diags
|
|
}
|
|
|
|
var providerBlockSchema = &hcl.BodySchema{
|
|
Attributes: []hcl.AttributeSchema{
|
|
{
|
|
Name: "alias",
|
|
},
|
|
{
|
|
Name: "version",
|
|
},
|
|
|
|
// Attribute names reserved for future expansion.
|
|
{Name: "count"},
|
|
{Name: "depends_on"},
|
|
{Name: "for_each"},
|
|
{Name: "source"},
|
|
},
|
|
Blocks: []hcl.BlockHeaderSchema{
|
|
// _All_ of these are reserved for future expansion.
|
|
{Type: "lifecycle"},
|
|
{Type: "locals"},
|
|
},
|
|
}
|