opentofu/terraform/graph_builder_apply_test.go
Martin Atkins 88b5607a7a core: Fetch schemas during context construction
Previously we fetched schemas during the AttachSchemaTransformer,
potentially multiple times as that was re-run for each graph built. Now
we fetch the schemas just once during context construction, passing that
result into each of the graph builders.

This only addresses the schema accesses during graph construction. We're
still separately loading schemas during the main walk for evaluation
purposes. This will be addressed in a later commit.
2018-10-16 18:49:20 -07:00

584 lines
13 KiB
Go

package terraform
import (
"strings"
"testing"
"github.com/hashicorp/terraform/addrs"
)
func TestApplyGraphBuilder_impl(t *testing.T) {
var _ GraphBuilder = new(ApplyGraphBuilder)
}
func TestApplyGraphBuilder(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
// Verify noop doesn't show up in graph
"test_object.noop": &InstanceDiff{},
"test_object.create": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
"test_object.other": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
&ModuleDiff{
Path: []string{"root", "child"},
Resources: map[string]*InstanceDiff{
"test_object.create": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
"test_object.other": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "graph-builder-apply-basic"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true,
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
if g.Path.String() != addrs.RootModuleInstance.String() {
t.Fatalf("wrong path %q", g.Path.String())
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testApplyGraphBuilderStr)
if actual != expected {
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
}
}
// This tests the ordering of two resources where a non-CBD depends
// on a CBD. GH-11349.
func TestApplyGraphBuilder_depCbd(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{"test_object.A": &InstanceDiff{Destroy: true,
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
RequiresNew: true,
},
},
},
"test_object.B": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "graph-builder-apply-dep-cbd"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true,
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
t.Logf("Graph: %s", g.String())
if g.Path.String() != addrs.RootModuleInstance.String() {
t.Fatalf("wrong path %q", g.Path.String())
}
// Create A, Modify B, Destroy A
testGraphHappensBefore(
t, g,
"test_object.A",
"test_object.A (destroy)")
testGraphHappensBefore(
t, g,
"test_object.A",
"test_object.B")
testGraphHappensBefore(
t, g,
"test_object.B",
"test_object.A (destroy)")
}
// This tests the ordering of two resources that are both CBD that
// require destroy/create.
func TestApplyGraphBuilder_doubleCBD(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
"test_object.A": &InstanceDiff{
Destroy: true,
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
"test_object.B": &InstanceDiff{
Destroy: true,
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "graph-builder-apply-double-cbd"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true,
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
if g.Path.String() != addrs.RootModuleInstance.String() {
t.Fatalf("wrong path %q", g.Path.String())
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testApplyGraphBuilderDoubleCBDStr)
if actual != expected {
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
}
}
// This tests the ordering of two resources being destroyed that depend
// on each other from only state. GH-11749
func TestApplyGraphBuilder_destroyStateOnly(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root", "child"},
Resources: map[string]*InstanceDiff{
"test_object.A": &InstanceDiff{
Destroy: true,
},
"test_object.B": &InstanceDiff{
Destroy: true,
},
},
},
},
}
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: []string{"root", "child"},
Resources: map[string]*ResourceState{
"test_object.A": &ResourceState{
Type: "test_object",
Primary: &InstanceState{
ID: "foo",
Attributes: map[string]string{},
},
},
"test_object.B": &ResourceState{
Type: "test_object",
Primary: &InstanceState{
ID: "bar",
Attributes: map[string]string{},
},
Dependencies: []string{"test_object.A"},
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "empty"),
Diff: diff,
State: state,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true,
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
t.Logf("Graph: %s", g.String())
if g.Path.String() != addrs.RootModuleInstance.String() {
t.Fatalf("wrong path %q", g.Path.String())
}
testGraphHappensBefore(
t, g,
"module.child.test_object.B (destroy)",
"module.child.test_object.A (destroy)")
}
// This tests the ordering of destroying a single count of a resource.
func TestApplyGraphBuilder_destroyCount(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
"test_object.A.1": &InstanceDiff{
Destroy: true,
},
"test_object.B": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"name": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "graph-builder-apply-count"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
DisableReduce: true,
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
if g.Path.String() != addrs.RootModuleInstance.String() {
t.Fatalf("wrong module path %q", g.Path)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testApplyGraphBuilderDestroyCountStr)
if actual != expected {
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
}
}
func TestApplyGraphBuilder_moduleDestroy(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root", "A"},
Resources: map[string]*InstanceDiff{
"test_object.foo": &InstanceDiff{
Destroy: true,
},
},
},
&ModuleDiff{
Path: []string{"root", "B"},
Resources: map[string]*InstanceDiff{
"test_object.foo": &InstanceDiff{
Destroy: true,
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "graph-builder-apply-module-destroy"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
testGraphHappensBefore(
t, g,
"module.B.test_object.foo (destroy)",
"module.A.test_object.foo (destroy)",
)
}
func TestApplyGraphBuilder_provisioner(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
"test_object.foo": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "graph-builder-apply-provisioner"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
testGraphContains(t, g, "provisioner.test")
testGraphHappensBefore(
t, g,
"provisioner.test",
"test_object.foo",
)
}
func TestApplyGraphBuilder_provisionerDestroy(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
"test_object.foo": &InstanceDiff{
Destroy: true,
},
},
},
},
}
b := &ApplyGraphBuilder{
Destroy: true,
Config: testModule(t, "graph-builder-apply-provisioner"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
testGraphContains(t, g, "provisioner.test")
testGraphHappensBefore(
t, g,
"provisioner.test",
"test_object.foo (destroy)",
)
}
func TestApplyGraphBuilder_targetModule(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
"test_object.foo": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
&ModuleDiff{
Path: []string{"root", "child2"},
Resources: map[string]*InstanceDiff{
"test_object.foo": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"test_string": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Config: testModule(t, "graph-builder-apply-target-module"),
Diff: diff,
Components: simpleMockComponentFactory(),
Schemas: simpleTestSchemas(),
Targets: []addrs.Targetable{
addrs.RootModuleInstance.Child("child2", addrs.NoKey),
},
}
g, err := b.Build(addrs.RootModuleInstance)
if err != nil {
t.Fatalf("err: %s", err)
}
testGraphNotContains(t, g, "module.child1.output.instance_id")
}
const testApplyGraphBuilderStr = `
meta.count-boundary (count boundary fixup)
module.child.provisioner.test
module.child.test_object.create
module.child.test_object.other
provider.test
test_object.create
test_object.other
module.child.provisioner.test
module.child.test_object.create
module.child.provisioner.test
provider.test
module.child.test_object.other
module.child.test_object.create
provider.test
provider.test
provider.test (close)
module.child.test_object.create
module.child.test_object.other
provider.test
test_object.create
test_object.other
provisioner.test (close)
module.child.test_object.create
root
meta.count-boundary (count boundary fixup)
provider.test (close)
provisioner.test (close)
test_object.create
provider.test
test_object.other
provider.test
test_object.create
`
const testApplyGraphBuilderDoubleCBDStr = `
meta.count-boundary (count boundary fixup)
provider.test
test_object.A
test_object.A (destroy)
test_object.B
test_object.B (destroy)
provider.test
provider.test (close)
provider.test
test_object.A
test_object.A (destroy)
test_object.B
test_object.B (destroy)
root
meta.count-boundary (count boundary fixup)
provider.test (close)
test_object.A
provider.test
test_object.A (destroy)
provider.test
test_object.A
test_object.B
test_object.B (destroy)
test_object.B
provider.test
test_object.A
test_object.A (destroy)
test_object.B (destroy)
provider.test
test_object.B
`
const testApplyGraphBuilderDestroyCountStr = `
meta.count-boundary (count boundary fixup)
provider.test
test_object.A[1] (destroy)
test_object.B
provider.test
provider.test (close)
provider.test
test_object.A[1] (destroy)
test_object.B
root
meta.count-boundary (count boundary fixup)
provider.test (close)
test_object.A[1] (destroy)
provider.test
test_object.B
provider.test
test_object.A[1] (destroy)
`