mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-02 12:17:39 -06:00
31349a9c3a
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
246 lines
6.9 KiB
Go
246 lines
6.9 KiB
Go
package configs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
version "github.com/hashicorp/go-version"
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// RequiredProvider represents a declaration of a dependency on a particular
|
|
// provider version or source without actually configuring that provider. This
|
|
// is used in child modules that expect a provider to be passed in from their
|
|
// parent.
|
|
type RequiredProvider struct {
|
|
Name string
|
|
Source string
|
|
Type addrs.Provider
|
|
Requirement VersionConstraint
|
|
DeclRange hcl.Range
|
|
Aliases []addrs.LocalProviderConfig
|
|
}
|
|
|
|
type RequiredProviders struct {
|
|
RequiredProviders map[string]*RequiredProvider
|
|
DeclRange hcl.Range
|
|
}
|
|
|
|
func decodeRequiredProvidersBlock(block *hcl.Block) (*RequiredProviders, hcl.Diagnostics) {
|
|
attrs, diags := block.Body.JustAttributes()
|
|
if diags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
|
|
ret := &RequiredProviders{
|
|
RequiredProviders: make(map[string]*RequiredProvider),
|
|
DeclRange: block.DefRange,
|
|
}
|
|
|
|
for name, attr := range attrs {
|
|
rp := &RequiredProvider{
|
|
Name: name,
|
|
DeclRange: attr.Expr.Range(),
|
|
}
|
|
|
|
// Look for a single static string, in case we have the legacy version-only
|
|
// format in the configuration.
|
|
if expr, err := attr.Expr.Value(nil); err == nil && expr.Type().IsPrimitiveType() {
|
|
vc, reqDiags := decodeVersionConstraint(attr)
|
|
diags = append(diags, reqDiags...)
|
|
|
|
pType, err := addrs.ParseProviderPart(rp.Name)
|
|
if err != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider name",
|
|
Detail: err.Error(),
|
|
Subject: attr.Expr.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
rp.Requirement = vc
|
|
rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType)
|
|
ret.RequiredProviders[name] = rp
|
|
|
|
continue
|
|
}
|
|
|
|
// verify that the local name is already localized or produce an error.
|
|
nameDiags := checkProviderNameNormalized(name, attr.Expr.Range())
|
|
if nameDiags.HasErrors() {
|
|
diags = append(diags, nameDiags...)
|
|
continue
|
|
}
|
|
|
|
kvs, mapDiags := hcl.ExprMap(attr.Expr)
|
|
if mapDiags.HasErrors() {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid required_providers object",
|
|
Detail: "required_providers entries must be strings or objects.",
|
|
Subject: attr.Expr.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
for _, kv := range kvs {
|
|
key, keyDiags := kv.Key.Value(nil)
|
|
if keyDiags.HasErrors() {
|
|
diags = append(diags, keyDiags...)
|
|
continue
|
|
}
|
|
|
|
if key.Type() != cty.String {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid Attribute",
|
|
Detail: fmt.Sprintf("Invalid attribute value for provider requirement: %#v", key),
|
|
Subject: kv.Key.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
switch key.AsString() {
|
|
case "version":
|
|
vc := VersionConstraint{
|
|
DeclRange: attr.Range,
|
|
}
|
|
|
|
constraint, valDiags := kv.Value.Value(nil)
|
|
if valDiags.HasErrors() || !constraint.Type().Equals(cty.String) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid version constraint",
|
|
Detail: "Version must be specified as a string.",
|
|
Subject: kv.Value.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
constraintStr := constraint.AsString()
|
|
constraints, err := version.NewConstraint(constraintStr)
|
|
if err != nil {
|
|
// NewConstraint doesn't return user-friendly errors, so we'll just
|
|
// ignore the provided error and produce our own generic one.
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid version constraint",
|
|
Detail: "This string does not use correct version constraint syntax.",
|
|
Subject: kv.Value.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
vc.Required = constraints
|
|
rp.Requirement = vc
|
|
|
|
case "source":
|
|
source, err := kv.Value.Value(nil)
|
|
if err != nil || !source.Type().Equals(cty.String) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid source",
|
|
Detail: "Source must be specified as a string.",
|
|
Subject: kv.Value.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
fqn, sourceDiags := addrs.ParseProviderSourceString(source.AsString())
|
|
if sourceDiags.HasErrors() {
|
|
hclDiags := sourceDiags.ToHCL()
|
|
// The diagnostics from ParseProviderSourceString don't contain
|
|
// source location information because it has no context to compute
|
|
// them from, and so we'll add those in quickly here before we
|
|
// return.
|
|
for _, diag := range hclDiags {
|
|
if diag.Subject == nil {
|
|
diag.Subject = kv.Value.Range().Ptr()
|
|
}
|
|
}
|
|
diags = append(diags, hclDiags...)
|
|
continue
|
|
}
|
|
|
|
rp.Source = source.AsString()
|
|
rp.Type = fqn
|
|
|
|
case "configuration_aliases":
|
|
exprs, listDiags := hcl.ExprList(kv.Value)
|
|
if listDiags.HasErrors() {
|
|
diags = append(diags, listDiags...)
|
|
continue
|
|
}
|
|
|
|
for _, expr := range exprs {
|
|
traversal, travDiags := hcl.AbsTraversalForExpr(expr)
|
|
if travDiags.HasErrors() {
|
|
diags = append(diags, travDiags...)
|
|
continue
|
|
}
|
|
|
|
addr, cfgDiags := ParseProviderConfigCompact(traversal)
|
|
if cfgDiags.HasErrors() {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid configuration_aliases value",
|
|
Detail: `Configuration aliases can only contain references to local provider configuration names in the format of provider.alias`,
|
|
Subject: kv.Value.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
if addr.LocalName != name {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid configuration_aliases value",
|
|
Detail: fmt.Sprintf(`Configuration aliases must be prefixed with the provider name. Expected %q, but found %q.`, name, addr.LocalName),
|
|
Subject: kv.Value.Range().Ptr(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
rp.Aliases = append(rp.Aliases, addr)
|
|
}
|
|
|
|
default:
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid required_providers object",
|
|
Detail: `required_providers objects can only contain "version", "source" and "configuration_aliases" attributes. To configure a provider, use a "provider" block.`,
|
|
Subject: kv.Key.Range().Ptr(),
|
|
})
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
if diags.HasErrors() {
|
|
continue
|
|
}
|
|
|
|
// We can add the required provider when there are no errors.
|
|
// If a source was not given, create an implied type.
|
|
if rp.Type.IsZero() {
|
|
pType, err := addrs.ParseProviderPart(rp.Name)
|
|
if err != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provider name",
|
|
Detail: err.Error(),
|
|
Subject: attr.Expr.Range().Ptr(),
|
|
})
|
|
} else {
|
|
rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType)
|
|
}
|
|
}
|
|
|
|
ret.RequiredProviders[rp.Name] = rp
|
|
}
|
|
|
|
return ret, diags
|
|
}
|