mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
bb118c37a2
Previously we were treating "dynamic" blocks in configuration the same as any other block type when merging config bodies, so that dynamic blocks in the override would override any dynamic blocks present in the base, without considering the dynamic block type. It's more useful and intuitive for us to treat dynamic blocks as if they are instances of their given block type for the purpose of overriding. That means a foo block can be overridden by a dynamic "foo" block and vice-versa, and dynamic blocks of different types do not interact at all during overriding. This requires us to recognize dynamic blocks and treat them specially during decoding of a merged body. We leave them unexpanded here because this package is not responsible for dynamic block expansion (that happens in the sibling "lang" package) but we do decode them enough to recognize their labels so we can treat them as if they were blocks of the labelled type.
202 lines
5.1 KiB
Go
202 lines
5.1 KiB
Go
package configs
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hashicorp/hcl2/gohcl"
|
|
"github.com/hashicorp/hcl2/hcl"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestModuleOverrideVariable(t *testing.T) {
|
|
mod, diags := testModuleFromDir("test-fixtures/valid-modules/override-variable")
|
|
assertNoDiagnostics(t, diags)
|
|
if mod == nil {
|
|
t.Fatalf("module is nil")
|
|
}
|
|
|
|
got := mod.Variables
|
|
want := map[string]*Variable{
|
|
"fully_overridden": {
|
|
Name: "fully_overridden",
|
|
Description: "b_override description",
|
|
DescriptionSet: true,
|
|
Default: cty.StringVal("b_override"),
|
|
Type: cty.String,
|
|
ParsingMode: VariableParseLiteral,
|
|
DeclRange: hcl.Range{
|
|
Filename: "test-fixtures/valid-modules/override-variable/primary.tf",
|
|
Start: hcl.Pos{
|
|
Line: 1,
|
|
Column: 1,
|
|
Byte: 0,
|
|
},
|
|
End: hcl.Pos{
|
|
Line: 1,
|
|
Column: 28,
|
|
Byte: 27,
|
|
},
|
|
},
|
|
},
|
|
"partially_overridden": {
|
|
Name: "partially_overridden",
|
|
Description: "base description",
|
|
DescriptionSet: true,
|
|
Default: cty.StringVal("b_override partial"),
|
|
Type: cty.String,
|
|
ParsingMode: VariableParseLiteral,
|
|
DeclRange: hcl.Range{
|
|
Filename: "test-fixtures/valid-modules/override-variable/primary.tf",
|
|
Start: hcl.Pos{
|
|
Line: 7,
|
|
Column: 1,
|
|
Byte: 103,
|
|
},
|
|
End: hcl.Pos{
|
|
Line: 7,
|
|
Column: 32,
|
|
Byte: 134,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
assertResultDeepEqual(t, got, want)
|
|
}
|
|
|
|
func TestModuleOverrideModule(t *testing.T) {
|
|
mod, diags := testModuleFromDir("test-fixtures/valid-modules/override-module")
|
|
assertNoDiagnostics(t, diags)
|
|
if mod == nil {
|
|
t.Fatalf("module is nil")
|
|
}
|
|
|
|
if _, exists := mod.ModuleCalls["example"]; !exists {
|
|
t.Fatalf("no module 'example'")
|
|
}
|
|
if len(mod.ModuleCalls) != 1 {
|
|
t.Fatalf("wrong number of module calls in result %d; want 1", len(mod.ModuleCalls))
|
|
}
|
|
|
|
got := mod.ModuleCalls["example"]
|
|
want := &ModuleCall{
|
|
Name: "example",
|
|
SourceAddr: "./example2-a_override",
|
|
SourceAddrRange: hcl.Range{
|
|
Filename: "test-fixtures/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,
|
|
DeclRange: hcl.Range{
|
|
Filename: "test-fixtures/valid-modules/override-module/primary.tf",
|
|
Start: hcl.Pos{
|
|
Line: 2,
|
|
Column: 1,
|
|
Byte: 1,
|
|
},
|
|
End: hcl.Pos{
|
|
Line: 2,
|
|
Column: 17,
|
|
Byte: 17,
|
|
},
|
|
},
|
|
}
|
|
|
|
// We're going to extract and nil out our hcl.Body here because DeepEqual
|
|
// is not a useful way to assert on that.
|
|
gotConfig := got.Config
|
|
got.Config = nil
|
|
|
|
assertResultDeepEqual(t, got, want)
|
|
|
|
type content struct {
|
|
Kept *string `hcl:"kept"`
|
|
Foo *string `hcl:"foo"`
|
|
New *string `hcl:"new"`
|
|
Newer *string `hcl:"newer"`
|
|
}
|
|
var gotArgs content
|
|
diags = gohcl.DecodeBody(gotConfig, nil, &gotArgs)
|
|
assertNoDiagnostics(t, diags)
|
|
|
|
wantArgs := content{
|
|
Kept: stringPtr("primary kept"),
|
|
Foo: stringPtr("a_override foo"),
|
|
New: stringPtr("b_override new"),
|
|
Newer: stringPtr("b_override newer"),
|
|
}
|
|
|
|
assertResultDeepEqual(t, gotArgs, wantArgs)
|
|
}
|
|
|
|
func TestModuleOverrideDynamic(t *testing.T) {
|
|
schema := &hcl.BodySchema{
|
|
Blocks: []hcl.BlockHeaderSchema{
|
|
{Type: "foo"},
|
|
{Type: "dynamic", LabelNames: []string{"type"}},
|
|
},
|
|
}
|
|
|
|
t.Run("base is dynamic", func(t *testing.T) {
|
|
mod, diags := testModuleFromDir("test-fixtures/valid-modules/override-dynamic-block-base")
|
|
assertNoDiagnostics(t, diags)
|
|
if mod == nil {
|
|
t.Fatalf("module is nil")
|
|
}
|
|
|
|
if _, exists := mod.ManagedResources["test.foo"]; !exists {
|
|
t.Fatalf("no module 'example'")
|
|
}
|
|
if len(mod.ManagedResources) != 1 {
|
|
t.Fatalf("wrong number of managed resources in result %d; want 1", len(mod.ManagedResources))
|
|
}
|
|
|
|
body := mod.ManagedResources["test.foo"].Config
|
|
content, diags := body.Content(schema)
|
|
assertNoDiagnostics(t, diags)
|
|
|
|
if len(content.Blocks) != 1 {
|
|
t.Fatalf("wrong number of blocks in result %d; want 1", len(content.Blocks))
|
|
}
|
|
if got, want := content.Blocks[0].Type, "foo"; got != want {
|
|
t.Fatalf("wrong block type %q; want %q", got, want)
|
|
}
|
|
})
|
|
t.Run("override is dynamic", func(t *testing.T) {
|
|
mod, diags := testModuleFromDir("test-fixtures/valid-modules/override-dynamic-block-override")
|
|
assertNoDiagnostics(t, diags)
|
|
if mod == nil {
|
|
t.Fatalf("module is nil")
|
|
}
|
|
|
|
if _, exists := mod.ManagedResources["test.foo"]; !exists {
|
|
t.Fatalf("no module 'example'")
|
|
}
|
|
if len(mod.ManagedResources) != 1 {
|
|
t.Fatalf("wrong number of managed resources in result %d; want 1", len(mod.ManagedResources))
|
|
}
|
|
|
|
body := mod.ManagedResources["test.foo"].Config
|
|
content, diags := body.Content(schema)
|
|
assertNoDiagnostics(t, diags)
|
|
|
|
if len(content.Blocks) != 1 {
|
|
t.Fatalf("wrong number of blocks in result %d; want 1", len(content.Blocks))
|
|
}
|
|
if got, want := content.Blocks[0].Type, "dynamic"; got != want {
|
|
t.Fatalf("wrong block type %q; want %q", got, want)
|
|
}
|
|
if got, want := content.Blocks[0].Labels[0], "foo"; got != want {
|
|
t.Fatalf("wrong dynamic block label %q; want %q", got, want)
|
|
}
|
|
})
|
|
}
|