Merge pull request #25369 from hashicorp/alisdair/fix-provider-requirements-panics

configs: Fix provider requirements panics
This commit is contained in:
Alisdair McDiarmid 2020-06-24 13:38:11 -04:00 committed by GitHub
commit 563abd55dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 25 deletions

View File

@ -4,6 +4,7 @@ import (
version "github.com/hashicorp/go-version" version "github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/zclconf/go-cty/cty"
) )
// RequiredProvider represents a declaration of a dependency on a particular // RequiredProvider represents a declaration of a dependency on a particular
@ -55,41 +56,61 @@ func decodeRequiredProvidersBlock(block *hcl.Block) (*RequiredProviders, hcl.Dia
vc := VersionConstraint{ vc := VersionConstraint{
DeclRange: attr.Range, DeclRange: attr.Range,
} }
constraintStr := expr.GetAttr("version").AsString() constraint := expr.GetAttr("version")
constraints, err := version.NewConstraint(constraintStr) if !constraint.Type().Equals(cty.String) || constraint.IsNull() {
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{ diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,
Summary: "Invalid version constraint", Summary: "Invalid version constraint",
Detail: "This string does not use correct version constraint syntax.", Detail: "Version must be specified as a string.",
Subject: attr.Expr.Range().Ptr(), Subject: attr.Expr.Range().Ptr(),
}) })
} else { } else {
vc.Required = constraints constraintStr := constraint.AsString()
rp.Requirement = vc 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: attr.Expr.Range().Ptr(),
})
} else {
vc.Required = constraints
rp.Requirement = vc
}
} }
} }
if expr.Type().HasAttribute("source") { if expr.Type().HasAttribute("source") {
rp.Source = expr.GetAttr("source").AsString() source := expr.GetAttr("source")
if !source.Type().Equals(cty.String) || source.IsNull() {
fqn, sourceDiags := addrs.ParseProviderSourceString(rp.Source) diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
if sourceDiags.HasErrors() { Summary: "Invalid source",
hclDiags := sourceDiags.ToHCL() Detail: "Source must be specified as a string.",
// The diagnostics from ParseProviderSourceString don't contain Subject: attr.Expr.Range().Ptr(),
// 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 = attr.Expr.Range().Ptr()
}
}
diags = append(diags, hclDiags...)
} else { } else {
rp.Type = fqn rp.Source = source.AsString()
fqn, sourceDiags := addrs.ParseProviderSourceString(rp.Source)
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 = attr.Expr.Range().Ptr()
}
}
diags = append(diags, hclDiags...)
} else {
rp.Type = fqn
}
} }
} }

View File

@ -306,6 +306,32 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) {
}, },
Error: "Invalid required_providers syntax", Error: "Invalid required_providers syntax",
}, },
"invalid source attribute type": {
Block: &hcl.Block{
Type: "required_providers",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcl.Attributes{
"my-test": {
Name: "my-test",
Expr: hcltest.MockExprLiteral(cty.ObjectVal(map[string]cty.Value{
"source": cty.DynamicVal,
})),
},
},
}),
DefRange: blockRange,
},
Want: &RequiredProviders{
RequiredProviders: map[string]*RequiredProvider{
"my-test": {
Name: "my-test",
DeclRange: mockRange,
},
},
DeclRange: blockRange,
},
Error: "Invalid source",
},
} }
for name, test := range tests { for name, test := range tests {