Skip to content

Commit

Permalink
allow parsing of required_providers containing ref
Browse files Browse the repository at this point in the history
The syntax for configuration_aliases contains bare references to match
their use in other parts of the configuration. These however cannot be
decoded directly without an EvalContext, as they represent variables.

Refactor decodeRequiredProvidersBlock to use the lower level ExprMap
function.
  • Loading branch information
jbardin committed Feb 8, 2021
1 parent 0c45ba3 commit e8f835a
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 e8f835a

Please sign in to comment.