mirror of
https://github.com/hashicorp/terraform.git
synced 2026-05-28 04:03:27 -04:00
Store module source and version expressions
Instead of evaluating and parsing a module source and version on configuration loading, we now simply store the expression. Decoding is now done during the graph-based configuration loading in the module install node.
This commit is contained in:
parent
28b76c1105
commit
11d819048a
5 changed files with 36 additions and 140 deletions
|
|
@ -7,26 +7,19 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/getmodules/moduleaddrs"
|
||||
)
|
||||
|
||||
// ModuleCall represents a "module" block in a module or file.
|
||||
type ModuleCall struct {
|
||||
Name string
|
||||
|
||||
SourceAddr addrs.ModuleSource
|
||||
SourceAddrRaw string
|
||||
SourceAddrRange hcl.Range
|
||||
SourceSet bool
|
||||
SourceExpr hcl.Expression
|
||||
|
||||
Config hcl.Body
|
||||
|
||||
Version VersionConstraint
|
||||
VersionExpr hcl.Expression
|
||||
|
||||
Count hcl.Expression
|
||||
ForEach hcl.Expression
|
||||
|
|
@ -66,75 +59,12 @@ func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagno
|
|||
})
|
||||
}
|
||||
|
||||
haveVersionArg := false
|
||||
if attr, exists := content.Attributes["version"]; exists {
|
||||
var versionDiags hcl.Diagnostics
|
||||
mc.Version, versionDiags = decodeVersionConstraint(attr)
|
||||
diags = append(diags, versionDiags...)
|
||||
haveVersionArg = true
|
||||
mc.VersionExpr = attr.Expr
|
||||
}
|
||||
|
||||
if attr, exists := content.Attributes["source"]; exists {
|
||||
mc.SourceSet = true
|
||||
mc.SourceAddrRange = attr.Expr.Range()
|
||||
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &mc.SourceAddrRaw)
|
||||
diags = append(diags, valDiags...)
|
||||
if !valDiags.HasErrors() {
|
||||
var addr addrs.ModuleSource
|
||||
var err error
|
||||
if haveVersionArg {
|
||||
addr, err = moduleaddrs.ParseModuleSourceRegistry(mc.SourceAddrRaw)
|
||||
} else {
|
||||
addr, err = moduleaddrs.ParseModuleSource(mc.SourceAddrRaw)
|
||||
}
|
||||
mc.SourceAddr = addr
|
||||
if err != nil {
|
||||
// NOTE: We leave mc.SourceAddr as nil for any situation where the
|
||||
// source attribute is invalid, so any code which tries to carefully
|
||||
// use the partial result of a failed config decode must be
|
||||
// resilient to that.
|
||||
mc.SourceAddr = nil
|
||||
|
||||
// NOTE: In practice it's actually very unlikely to end up here,
|
||||
// because our source address parser can turn just about any string
|
||||
// into some sort of remote package address, and so for most errors
|
||||
// we'll detect them only during module installation. There are
|
||||
// still a _few_ purely-syntax errors we can catch at parsing time,
|
||||
// though, mostly related to remote package sub-paths and local
|
||||
// paths.
|
||||
switch err := err.(type) {
|
||||
case *moduleaddrs.MaybeRelativePathErr:
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid module source address",
|
||||
Detail: fmt.Sprintf(
|
||||
"Terraform failed to determine your intended installation method for remote module package %q.\n\nIf you intended this as a path relative to the current module, use \"./%s\" instead. The \"./\" prefix indicates that the address is a relative filesystem path.",
|
||||
err.Addr, err.Addr,
|
||||
),
|
||||
Subject: mc.SourceAddrRange.Ptr(),
|
||||
})
|
||||
default:
|
||||
if haveVersionArg {
|
||||
// In this case we'll include some extra context that
|
||||
// we assumed a registry source address due to the
|
||||
// version argument.
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid registry module source address",
|
||||
Detail: fmt.Sprintf("Failed to parse module registry address: %s.\n\nTerraform assumed that you intended a module registry source address because you also set the argument \"version\", which applies only to registry modules.", err),
|
||||
Subject: mc.SourceAddrRange.Ptr(),
|
||||
})
|
||||
} else {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid module source address",
|
||||
Detail: fmt.Sprintf("Failed to parse module source address: %s.", err),
|
||||
Subject: mc.SourceAddrRange.Ptr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mc.SourceExpr = attr.Expr
|
||||
}
|
||||
|
||||
if attr, exists := content.Attributes["count"]; exists {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
)
|
||||
|
||||
func TestLoadModuleCall(t *testing.T) {
|
||||
|
|
@ -31,15 +31,11 @@ func TestLoadModuleCall(t *testing.T) {
|
|||
gotModules := file.ModuleCalls
|
||||
wantModules := []*ModuleCall{
|
||||
{
|
||||
Name: "foo",
|
||||
SourceAddr: addrs.ModuleSourceLocal("./foo"),
|
||||
SourceAddrRaw: "./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},
|
||||
},
|
||||
Name: "foo",
|
||||
SourceExpr: mustExpr(hclsyntax.ParseExpression(
|
||||
[]byte("\"./foo\""), "module-calls.tf",
|
||||
hcl.Pos{Line: 3, Column: 12, Byte: 27},
|
||||
)),
|
||||
DeclRange: hcl.Range{
|
||||
Filename: "module-calls.tf",
|
||||
Start: hcl.Pos{Line: 2, Column: 1, Byte: 1},
|
||||
|
|
@ -48,21 +44,10 @@ func TestLoadModuleCall(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "bar",
|
||||
SourceAddr: addrs.ModuleSourceRegistry{
|
||||
Package: addrs.ModuleRegistryPackage{
|
||||
Host: addrs.DefaultModuleRegistryHost,
|
||||
Namespace: "hashicorp",
|
||||
Name: "bar",
|
||||
TargetSystem: "aws",
|
||||
},
|
||||
},
|
||||
SourceAddrRaw: "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},
|
||||
},
|
||||
SourceExpr: mustExpr(hclsyntax.ParseExpression(
|
||||
[]byte("\"hashicorp/bar/aws\""), "module-calls.tf",
|
||||
hcl.Pos{Line: 8, Column: 12, Byte: 113},
|
||||
)),
|
||||
DeclRange: hcl.Range{
|
||||
Filename: "module-calls.tf",
|
||||
Start: hcl.Pos{Line: 7, Column: 1, Byte: 87},
|
||||
|
|
@ -71,16 +56,10 @@ func TestLoadModuleCall(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "baz",
|
||||
SourceAddr: addrs.ModuleSourceRemote{
|
||||
Package: addrs.ModulePackage("git::https://example.com/"),
|
||||
},
|
||||
SourceAddrRaw: "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},
|
||||
},
|
||||
SourceExpr: mustExpr(hclsyntax.ParseExpression(
|
||||
[]byte("\"git::https://example.com/\""), "module-calls.tf",
|
||||
hcl.Pos{Line: 15, Column: 12, Byte: 193},
|
||||
)),
|
||||
DependsOn: []hcl.Traversal{
|
||||
{
|
||||
hcl.TraverseRoot{
|
||||
|
|
|
|||
|
|
@ -178,11 +178,8 @@ func (o *Output) merge(oo *Output) hcl.Diagnostics {
|
|||
func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
if omc.SourceSet {
|
||||
mc.SourceAddr = omc.SourceAddr
|
||||
mc.SourceAddrRaw = omc.SourceAddrRaw
|
||||
mc.SourceAddrRange = omc.SourceAddrRange
|
||||
mc.SourceSet = omc.SourceSet
|
||||
if omc.SourceExpr != nil {
|
||||
mc.SourceExpr = omc.SourceExpr
|
||||
}
|
||||
|
||||
if omc.Count != nil {
|
||||
|
|
@ -193,8 +190,8 @@ func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics {
|
|||
mc.ForEach = omc.ForEach
|
||||
}
|
||||
|
||||
if len(omc.Version.Required) != 0 {
|
||||
mc.Version = omc.Version
|
||||
if omc.VersionExpr != nil {
|
||||
mc.VersionExpr = omc.VersionExpr
|
||||
}
|
||||
|
||||
mc.Config = MergeBodies(mc.Config, omc.Config)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
|
|
@ -91,23 +92,11 @@ func TestModuleOverrideModule(t *testing.T) {
|
|||
|
||||
got := mod.ModuleCalls["example"]
|
||||
want := &ModuleCall{
|
||||
Name: "example",
|
||||
SourceAddr: addrs.ModuleSourceLocal("./example2-a_override"),
|
||||
SourceAddrRaw: "./example2-a_override",
|
||||
SourceAddrRange: hcl.Range{
|
||||
Filename: "testdata/valid-modules/override-module/a_override.tf",
|
||||
Start: hcl.Pos{
|
||||
Line: 3,
|
||||
Column: 12,
|
||||
Byte: 31,
|
||||
},
|
||||
End: hcl.Pos{
|
||||
Line: 3,
|
||||
Column: 35,
|
||||
Byte: 54,
|
||||
},
|
||||
},
|
||||
SourceSet: true,
|
||||
Name: "example",
|
||||
SourceExpr: mustExpr(hclsyntax.ParseExpression(
|
||||
[]byte("\"./example2-a_override\""), "testdata/valid-modules/override-module/a_override.tf",
|
||||
hcl.Pos{Line: 3, Column: 12, Byte: 31},
|
||||
)),
|
||||
DeclRange: hcl.Range{
|
||||
Filename: "testdata/valid-modules/override-module/primary.tf",
|
||||
Start: hcl.Pos{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
)
|
||||
|
|
@ -253,14 +254,14 @@ func validateProviderConfigsForTests(cfg *Config) (diags hcl.Diagnostics) {
|
|||
|
||||
// Let's make a little fake module call that we can use to call
|
||||
// into validateProviderConfigs.
|
||||
sourceExpr := hcl.StaticExpr(cty.StringVal(run.Module.Source.String()), run.Module.SourceDeclRange)
|
||||
versionExpr := hcl.StaticExpr(cty.StringVal(run.Module.Version.Required.String()), run.Module.Version.DeclRange)
|
||||
mc := &ModuleCall{
|
||||
Name: run.Name,
|
||||
SourceAddr: run.Module.Source,
|
||||
SourceAddrRange: run.Module.SourceDeclRange,
|
||||
SourceSet: true,
|
||||
Version: run.Module.Version,
|
||||
Providers: providers,
|
||||
DeclRange: run.Module.DeclRange,
|
||||
Name: run.Name,
|
||||
SourceExpr: sourceExpr,
|
||||
VersionExpr: versionExpr,
|
||||
Providers: providers,
|
||||
DeclRange: run.Module.DeclRange,
|
||||
}
|
||||
|
||||
diags = append(diags, validateProviderConfigs(mc, run.ConfigUnderTest, nil)...)
|
||||
|
|
|
|||
Loading…
Reference in a new issue