Skip to content

Commit

Permalink
Merge pull request hashicorp#59 from hashicorp/jbardin/required-provi…
Browse files Browse the repository at this point in the history
…ders

allow parsing of required_providers containing ref
  • Loading branch information
jbardin authored Feb 9, 2021
2 parents 0c45ba3 + e8f835a commit 4fd17a0
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 35 deletions.
86 changes: 55 additions & 31 deletions tfconfig/provider_ref.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package tfconfig

import (
"fmt"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/zclconf/go-cty/cty/gocty"
"github.com/zclconf/go-cty/cty"
)

// ProviderRef is a reference to a provider configuration within a module.
Expand All @@ -23,13 +25,8 @@ func decodeRequiredProvidersBlock(block *hcl.Block) (map[string]*ProviderRequire
attrs, diags := block.Body.JustAttributes()
reqs := make(map[string]*ProviderRequirement)
for name, attr := range attrs {
expr, err := attr.Expr.Value(nil)
if err != nil {
diags = append(diags, err...)
}

switch {
case expr.Type().IsPrimitiveType():
// Look for a legacy version in the attribute first
if expr, err := attr.Expr.Value(nil); err == nil && expr.Type().IsPrimitiveType() {
var version string
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &version)
diags = append(diags, valDiags...)
Expand All @@ -38,46 +35,73 @@ func decodeRequiredProvidersBlock(block *hcl.Block) (map[string]*ProviderRequire
VersionConstraints: []string{version},
}
}
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
}

var pr ProviderRequirement

for _, kv := range kvs {
key, keyDiags := kv.Key.Value(nil)
if keyDiags.HasErrors() {
diags = append(diags, keyDiags...)
continue
}

case expr.Type().IsObjectType():
var pr ProviderRequirement
if expr.Type().HasAttribute("version") {
var version string
err := gocty.FromCtyValue(expr.GetAttr("version"), &version)
if err == nil {
pr.VersionConstraints = append(pr.VersionConstraints, version)
} else {
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":
version, valDiags := kv.Value.Value(nil)
if valDiags.HasErrors() || !version.Type().Equals(cty.String) {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Unsuitable value type",
Detail: "Unsuitable value: string required",
Subject: attr.Expr.Range().Ptr(),
})
continue
}
}
if expr.Type().HasAttribute("source") {
var source string
err := gocty.FromCtyValue(expr.GetAttr("source"), &source)
if err == nil {
pr.Source = source
} else {
if !version.IsNull() {
pr.VersionConstraints = append(pr.VersionConstraints, version.AsString())
}

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: "Unsuitable value type",
Detail: "Unsuitable value: string required",
Subject: attr.Expr.Range().Ptr(),
})
continue
}

if !source.IsNull() {
pr.Source = source.AsString()
}
}
reqs[name] = &pr

default:
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Unsuitable value type",
Detail: "Unsuitable value: string required",
Subject: attr.Expr.Range().Ptr(),
})
reqs[name] = &pr
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"required_providers": {
"bar": {},
"baz": {},
"bleep": {},
"empty": {},
"foo": {}
},
Expand Down
1 change: 1 addition & 0 deletions tfconfig/testdata/provider-aliases/provider-aliases.out.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Provider Requirements:
* **bar:** (any version)
* **baz:** (any version)
* **bleep:** (any version)
* **empty:** (any version)
* **foo:** (any version)

8 changes: 8 additions & 0 deletions tfconfig/testdata/provider-aliases/provider-aliases.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
terraform {
required_providers {
bleep = {
configuration_aliases = [ bleep.bloop ]
}
}
}

provider "foo" {
alias = "blue"
}
Expand Down
4 changes: 2 additions & 2 deletions tfconfig/testdata/type-errors/type-errors.out.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
},
{
"severity": "error",
"summary": "Unsuitable value type",
"detail": "Unsuitable value: string required",
"summary": "Invalid required_providers object",
"detail": "Required providers entries must be strings or objects.",
"pos": {
"filename": "testdata/type-errors/type-errors.tf",
"line": 27
Expand Down
4 changes: 2 additions & 2 deletions tfconfig/testdata/type-errors/type-errors.out.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ Provider argument requires a provider name followed by an optional alias, like "

Unsuitable value: string required

## Error: Unsuitable value type
## Error: Invalid required_providers object

(at `testdata/type-errors/type-errors.tf` line 27)

Unsuitable value: string required
Required providers entries must be strings or objects.

0 comments on commit 4fd17a0

Please sign in to comment.