configs: parse the "providers" map for module calls
This was accidentally missed on the first pass of module call decoding. As before, this is a map from child provider config address to parent provider config address, allowing the set of providers to be projected in arbitrary ways into a child module.
This commit is contained in:
parent
c6598a3f86
commit
b6fdd0446e
|
@ -1,6 +1,8 @@
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl2/gohcl"
|
"github.com/hashicorp/hcl2/gohcl"
|
||||||
"github.com/hashicorp/hcl2/hcl"
|
"github.com/hashicorp/hcl2/hcl"
|
||||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||||
|
@ -21,6 +23,8 @@ type ModuleCall struct {
|
||||||
Count hcl.Expression
|
Count hcl.Expression
|
||||||
ForEach hcl.Expression
|
ForEach hcl.Expression
|
||||||
|
|
||||||
|
Providers []PassedProviderConfig
|
||||||
|
|
||||||
DependsOn []hcl.Traversal
|
DependsOn []hcl.Traversal
|
||||||
|
|
||||||
DeclRange hcl.Range
|
DeclRange hcl.Range
|
||||||
|
@ -76,9 +80,49 @@ func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagno
|
||||||
mc.DependsOn = append(mc.DependsOn, deps...)
|
mc.DependsOn = append(mc.DependsOn, deps...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if attr, exists := content.Attributes["providers"]; exists {
|
||||||
|
seen := make(map[string]hcl.Range)
|
||||||
|
pairs, pDiags := hcl.ExprMap(attr.Expr)
|
||||||
|
diags = append(diags, pDiags...)
|
||||||
|
for _, pair := range pairs {
|
||||||
|
key, keyDiags := decodeProviderConfigRef(pair.Key, "providers")
|
||||||
|
diags = append(diags, keyDiags...)
|
||||||
|
value, valueDiags := decodeProviderConfigRef(pair.Value, "providers")
|
||||||
|
diags = append(diags, valueDiags...)
|
||||||
|
if keyDiags.HasErrors() || valueDiags.HasErrors() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matchKey := key.String()
|
||||||
|
if prev, exists := seen[matchKey]; exists {
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Duplicate provider address",
|
||||||
|
Detail: fmt.Sprintf("A provider configuration was already passed to %s at %s. Each child provider configuration can be assigned only once.", matchKey, prev),
|
||||||
|
Subject: pair.Value.Range().Ptr(),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rng := hcl.RangeBetween(pair.Key.Range(), pair.Value.Range())
|
||||||
|
seen[matchKey] = rng
|
||||||
|
mc.Providers = append(mc.Providers, PassedProviderConfig{
|
||||||
|
InChild: key,
|
||||||
|
InParent: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return mc, diags
|
return mc, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PassedProviderConfig represents a provider config explicitly passed down to
|
||||||
|
// a child module, possibly giving it a new local address in the process.
|
||||||
|
type PassedProviderConfig struct {
|
||||||
|
InChild *ProviderConfigRef
|
||||||
|
InParent *ProviderConfigRef
|
||||||
|
}
|
||||||
|
|
||||||
var moduleBlockSchema = &hcl.BodySchema{
|
var moduleBlockSchema = &hcl.BodySchema{
|
||||||
Attributes: []hcl.AttributeSchema{
|
Attributes: []hcl.AttributeSchema{
|
||||||
{
|
{
|
||||||
|
@ -97,5 +141,8 @@ var moduleBlockSchema = &hcl.BodySchema{
|
||||||
{
|
{
|
||||||
Name: "depends_on",
|
Name: "depends_on",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "providers",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-test/deep"
|
||||||
|
"github.com/hashicorp/hcl2/hcl"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadModuleCall(t *testing.T) {
|
||||||
|
src, err := ioutil.ReadFile("test-fixtures/valid-files/module-calls.tf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := testParser(map[string]string{
|
||||||
|
"module-calls.tf": string(src),
|
||||||
|
})
|
||||||
|
|
||||||
|
file, diags := parser.LoadConfigFile("module-calls.tf")
|
||||||
|
if len(diags) != 0 {
|
||||||
|
t.Errorf("Wrong number of diagnostics %d; want 0", len(diags))
|
||||||
|
for _, diag := range diags {
|
||||||
|
t.Logf("- %s", diag)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gotModules := file.ModuleCalls
|
||||||
|
wantModules := []*ModuleCall{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
SourceAddr: "./foo",
|
||||||
|
SourceSet: true,
|
||||||
|
SourceAddrRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 3, Column: 12, Byte: 27},
|
||||||
|
End: hcl.Pos{Line: 3, Column: 19, Byte: 34},
|
||||||
|
},
|
||||||
|
DeclRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 2, Column: 1, Byte: 1},
|
||||||
|
End: hcl.Pos{Line: 2, Column: 13, Byte: 13},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
SourceAddr: "hashicorp/bar/aws",
|
||||||
|
SourceSet: true,
|
||||||
|
SourceAddrRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 8, Column: 12, Byte: 113},
|
||||||
|
End: hcl.Pos{Line: 8, Column: 31, Byte: 132},
|
||||||
|
},
|
||||||
|
DeclRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 7, Column: 1, Byte: 87},
|
||||||
|
End: hcl.Pos{Line: 7, Column: 13, Byte: 99},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "baz",
|
||||||
|
SourceAddr: "git::https://example.com/",
|
||||||
|
SourceSet: true,
|
||||||
|
SourceAddrRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 15, Column: 12, Byte: 193},
|
||||||
|
End: hcl.Pos{Line: 15, Column: 39, Byte: 220},
|
||||||
|
},
|
||||||
|
DependsOn: []hcl.Traversal{
|
||||||
|
{
|
||||||
|
hcl.TraverseRoot{
|
||||||
|
Name: "module",
|
||||||
|
SrcRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 23, Column: 5, Byte: 295},
|
||||||
|
End: hcl.Pos{Line: 23, Column: 11, Byte: 301},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hcl.TraverseAttr{
|
||||||
|
Name: "bar",
|
||||||
|
SrcRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 23, Column: 11, Byte: 301},
|
||||||
|
End: hcl.Pos{Line: 23, Column: 15, Byte: 305},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Providers: []PassedProviderConfig{
|
||||||
|
{
|
||||||
|
InChild: &ProviderConfigRef{
|
||||||
|
Name: "aws",
|
||||||
|
NameRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 27, Column: 5, Byte: 332},
|
||||||
|
End: hcl.Pos{Line: 27, Column: 8, Byte: 335},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InParent: &ProviderConfigRef{
|
||||||
|
Name: "aws",
|
||||||
|
NameRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 27, Column: 11, Byte: 338},
|
||||||
|
End: hcl.Pos{Line: 27, Column: 14, Byte: 341},
|
||||||
|
},
|
||||||
|
Alias: "foo",
|
||||||
|
AliasRange: &hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 27, Column: 14, Byte: 341},
|
||||||
|
End: hcl.Pos{Line: 27, Column: 18, Byte: 345},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DeclRange: hcl.Range{
|
||||||
|
Filename: "module-calls.tf",
|
||||||
|
Start: hcl.Pos{Line: 14, Column: 1, Byte: 167},
|
||||||
|
End: hcl.Pos{Line: 14, Column: 13, Byte: 179},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll hide all of the bodies/exprs since we're treating them as opaque
|
||||||
|
// here anyway... the point of this test is to ensure we handle everything
|
||||||
|
// else properly.
|
||||||
|
for _, m := range gotModules {
|
||||||
|
m.Config = nil
|
||||||
|
m.Count = nil
|
||||||
|
m.ForEach = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, problem := range deep.Equal(gotModules, wantModules) {
|
||||||
|
t.Error(problem)
|
||||||
|
}
|
||||||
|
}
|
|
@ -114,7 +114,7 @@ func decodeResourceBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) {
|
||||||
|
|
||||||
if attr, exists := content.Attributes["provider"]; exists {
|
if attr, exists := content.Attributes["provider"]; exists {
|
||||||
var providerDiags hcl.Diagnostics
|
var providerDiags hcl.Diagnostics
|
||||||
r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr)
|
r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr.Expr, "provider")
|
||||||
diags = append(diags, providerDiags...)
|
diags = append(diags, providerDiags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ func decodeDataBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) {
|
||||||
|
|
||||||
if attr, exists := content.Attributes["provider"]; exists {
|
if attr, exists := content.Attributes["provider"]; exists {
|
||||||
var providerDiags hcl.Diagnostics
|
var providerDiags hcl.Diagnostics
|
||||||
r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr)
|
r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr.Expr, "provider")
|
||||||
diags = append(diags, providerDiags...)
|
diags = append(diags, providerDiags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,10 +324,11 @@ type ProviderConfigRef struct {
|
||||||
AliasRange *hcl.Range // nil if alias not set
|
AliasRange *hcl.Range // nil if alias not set
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeProviderConfigRef(attr *hcl.Attribute) (*ProviderConfigRef, hcl.Diagnostics) {
|
func decodeProviderConfigRef(expr hcl.Expression, argName string) (*ProviderConfigRef, hcl.Diagnostics) {
|
||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
expr, shimDiags := shimTraversalInString(attr.Expr, false)
|
var shimDiags hcl.Diagnostics
|
||||||
|
expr, shimDiags = shimTraversalInString(expr, false)
|
||||||
diags = append(diags, shimDiags...)
|
diags = append(diags, shimDiags...)
|
||||||
|
|
||||||
traversal, travDiags := hcl.AbsTraversalForExpr(expr)
|
traversal, travDiags := hcl.AbsTraversalForExpr(expr)
|
||||||
|
@ -345,7 +346,7 @@ func decodeProviderConfigRef(attr *hcl.Attribute) (*ProviderConfigRef, hcl.Diagn
|
||||||
// showing that usage, so we'll sniff for that situation here and
|
// showing that usage, so we'll sniff for that situation here and
|
||||||
// produce a specialized error message for it to help users find
|
// produce a specialized error message for it to help users find
|
||||||
// the new correct form.
|
// the new correct form.
|
||||||
if exprIsNativeQuotedString(attr.Expr) {
|
if exprIsNativeQuotedString(expr) {
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Invalid provider configuration reference",
|
Summary: "Invalid provider configuration reference",
|
||||||
|
@ -358,7 +359,7 @@ func decodeProviderConfigRef(attr *hcl.Attribute) (*ProviderConfigRef, hcl.Diagn
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Invalid provider configuration reference",
|
Summary: "Invalid provider configuration reference",
|
||||||
Detail: fmt.Sprintf("The %s argument requires a provider type name, optionally followed by a period and then a configuration alias.", attr.Name),
|
Detail: fmt.Sprintf("The %s argument requires a provider type name, optionally followed by a period and then a configuration alias.", argName),
|
||||||
Subject: expr.Range().Ptr(),
|
Subject: expr.Range().Ptr(),
|
||||||
})
|
})
|
||||||
return nil, diags
|
return nil, diags
|
||||||
|
|
|
@ -22,5 +22,8 @@ module "baz" {
|
||||||
depends_on = [
|
depends_on = [
|
||||||
module.bar,
|
module.bar,
|
||||||
]
|
]
|
||||||
}
|
|
||||||
|
|
||||||
|
providers = {
|
||||||
|
aws = aws.foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue