diff --git a/configs/provider_requirements.go b/configs/provider_requirements.go index 361fec5eb..74188ed81 100644 --- a/configs/provider_requirements.go +++ b/configs/provider_requirements.go @@ -4,6 +4,7 @@ import ( version "github.com/hashicorp/go-version" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/addrs" + "github.com/zclconf/go-cty/cty" ) // RequiredProvider represents a declaration of a dependency on a particular @@ -55,41 +56,61 @@ func decodeRequiredProvidersBlock(block *hcl.Block) (*RequiredProviders, hcl.Dia vc := VersionConstraint{ DeclRange: attr.Range, } - constraintStr := expr.GetAttr("version").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. + constraint := expr.GetAttr("version") + if !constraint.Type().Equals(cty.String) || constraint.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, 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(), }) } else { - vc.Required = constraints - rp.Requirement = vc + 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: attr.Expr.Range().Ptr(), + }) + } else { + vc.Required = constraints + rp.Requirement = vc + } } } if expr.Type().HasAttribute("source") { - rp.Source = expr.GetAttr("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...) + source := expr.GetAttr("source") + if !source.Type().Equals(cty.String) || source.IsNull() { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid source", + Detail: "Source must be specified as a string.", + Subject: attr.Expr.Range().Ptr(), + }) } 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 + } } } diff --git a/configs/provider_requirements_test.go b/configs/provider_requirements_test.go index 3d1da1a23..42e4bfbe1 100644 --- a/configs/provider_requirements_test.go +++ b/configs/provider_requirements_test.go @@ -306,6 +306,32 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { }, 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 {