configs: Include "moved" blocks when merging multiple files into a module
An earlier commit added logic to decode "moved" blocks and do static validation of them. Here we now include that result also in modules produced from those files, which we can then use in Terraform Core to actually implement the moves. This also places the feature behind an active experiment keyword called config_driven_move. For now activating this doesn't actually achieve anything except let you include moved blocks that Terraform will summarily ignore, but we'll expand the scope of this in later commits to eventually reach the point where it's really usable.
This commit is contained in:
parent
d92b5e5f5e
commit
6b8e103d6a
|
@ -197,6 +197,17 @@ func checkModuleExperiments(m *Module) hcl.Diagnostics {
|
|||
}
|
||||
}
|
||||
|
||||
if !m.ActiveExperiments.Has(experiments.ConfigDrivenMove) {
|
||||
for _, mc := range m.Moved {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Config-driven move is experimental",
|
||||
Detail: "This feature is currently under development and is not yet fully-functional.\n\nIf you'd like to try the partial implementation that exists so far, add config_driven_move to the set of active experiments for this module.",
|
||||
Subject: mc.DeclRange.Ptr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ type Module struct {
|
|||
|
||||
ManagedResources map[string]*Resource
|
||||
DataResources map[string]*Resource
|
||||
|
||||
Moved []*Moved
|
||||
}
|
||||
|
||||
// File describes the contents of a single configuration file.
|
||||
|
@ -330,6 +332,11 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
|
|||
}
|
||||
}
|
||||
|
||||
// "Moved" blocks just append, because they are all independent
|
||||
// of one another at this level. (We handle any references between
|
||||
// them at runtime.)
|
||||
m.Moved = append(m.Moved, file.Moved...)
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
|
@ -484,6 +491,15 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics {
|
|||
diags = append(diags, mergeDiags...)
|
||||
}
|
||||
|
||||
for _, m := range file.Moved {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Cannot override 'moved' blocks",
|
||||
Detail: "Records of moved objects can appear only in normal files, not in override files.",
|
||||
Subject: m.DeclRange.Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
|
|
|
@ -169,6 +169,30 @@ func TestDecodeMovedBlock(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMovedBlocksInModule(t *testing.T) {
|
||||
parser := NewParser(nil)
|
||||
mod, diags := parser.LoadConfigDir("testdata/valid-modules/moved-blocks")
|
||||
if diags.HasErrors() {
|
||||
t.Errorf("unexpected error: %s", diags.Error())
|
||||
}
|
||||
|
||||
var gotPairs [][2]string
|
||||
for _, mc := range mod.Moved {
|
||||
gotPairs = append(gotPairs, [2]string{mc.From.String(), mc.To.String()})
|
||||
}
|
||||
wantPairs := [][2]string{
|
||||
{`test.foo`, `test.bar`},
|
||||
{`test.foo`, `test.bar["bloop"]`},
|
||||
{`module.a`, `module.b`},
|
||||
{`module.a`, `module.a["foo"]`},
|
||||
{`test.foo`, `module.a.test.foo`},
|
||||
{`data.test.foo`, `data.test.bar`},
|
||||
}
|
||||
if diff := cmp.Diff(wantPairs, gotPairs); diff != "" {
|
||||
t.Errorf("wrong addresses\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func mustMoveEndpointFromExpr(expr hcl.Expression) *addrs.MoveEndpoint {
|
||||
traversal, hcldiags := hcl.AbsTraversalForExpr(expr)
|
||||
if hcldiags.HasErrors() {
|
||||
|
|
|
@ -2,7 +2,6 @@ package configs
|
|||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/terraform/internal/experiments"
|
||||
)
|
||||
|
||||
// LoadConfigFile reads the file at the given path and parses it as a config
|
||||
|
@ -150,17 +149,11 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
|
|||
}
|
||||
|
||||
case "moved":
|
||||
// This is not quite the usual usage of the experiments package.
|
||||
// EverythingIsAPlan is not registered as an active experiment, so
|
||||
// this block will not be decoded until either the experiment is
|
||||
// registered, or this check is dropped altogether.
|
||||
if file.ActiveExperiments.Has(experiments.EverythingIsAPlan) {
|
||||
cfg, cfgDiags := decodeMovedBlock(block)
|
||||
diags = append(diags, cfgDiags...)
|
||||
if cfg != nil {
|
||||
file.Moved = append(file.Moved, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// Should never happen because the above cases should be exhaustive
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# This is currently invalid alone because config-driven move requires the
|
||||
# "config_driven_move" experiment. We can remove this test case altogther
|
||||
# if moved blocks of this sort graduate to being stable.
|
||||
moved {
|
||||
from = a.b
|
||||
to = c.d
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
terraform {
|
||||
# For the moment this is experimental
|
||||
experiments = [config_driven_move]
|
||||
}
|
||||
|
||||
moved {
|
||||
from = test.foo
|
||||
to = test.bar
|
||||
}
|
||||
|
||||
moved {
|
||||
from = test.foo
|
||||
to = test.bar["bloop"]
|
||||
}
|
||||
|
||||
moved {
|
||||
from = module.a
|
||||
to = module.b
|
||||
}
|
||||
|
||||
moved {
|
||||
from = module.a
|
||||
to = module.a["foo"]
|
||||
}
|
||||
|
||||
moved {
|
||||
from = test.foo
|
||||
to = module.a.test.foo
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
# One more moved block in a separate file just to make sure the
|
||||
# appending of multiple files works properly.
|
||||
moved {
|
||||
from = data.test.foo
|
||||
to = data.test.bar
|
||||
}
|
|
@ -16,7 +16,7 @@ const (
|
|||
VariableValidation = Experiment("variable_validation")
|
||||
ModuleVariableOptionalAttrs = Experiment("module_variable_optional_attrs")
|
||||
SuppressProviderSensitiveAttrs = Experiment("provider_sensitive_attrs")
|
||||
EverythingIsAPlan = Experiment("everything_is_a_plan")
|
||||
ConfigDrivenMove = Experiment("config_driven_move")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -25,6 +25,7 @@ func init() {
|
|||
registerConcludedExperiment(VariableValidation, "Custom variable validation can now be used by default, without enabling an experiment.")
|
||||
registerConcludedExperiment(SuppressProviderSensitiveAttrs, "Provider-defined sensitive attributes are now redacted by default, without enabling an experiment.")
|
||||
registerCurrentExperiment(ModuleVariableOptionalAttrs)
|
||||
registerCurrentExperiment(ConfigDrivenMove)
|
||||
}
|
||||
|
||||
// GetCurrent takes an experiment name and returns the experiment value
|
||||
|
|
Loading…
Reference in New Issue