mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-06 14:13:16 -06:00
38ec730b0e
Previously the graph builders all expected to be given a full manifest of all of the plugin component schemas that they could need during their analysis work. That made sense when terraform.NewContext would always proactively load all of the schemas before doing any other work, but we now have a load-as-needed strategy for schemas. We'll now have the graph builders use the contextPlugins object they each already hold to retrieve individual schemas when needed. This avoids the need to prepare a redundant data structure to pass alongside the contextPlugins object, and leans on the memoization behavior inside contextPlugins to preserve the old behavior of loading each provider's schema only once.
362 lines
9.7 KiB
Go
362 lines
9.7 KiB
Go
package terraform
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
)
|
|
|
|
func cbdTestGraph(t *testing.T, mod string, changes *plans.Changes, state *states.State) *Graph {
|
|
module := testModule(t, mod)
|
|
|
|
applyBuilder := &ApplyGraphBuilder{
|
|
Config: module,
|
|
Changes: changes,
|
|
Plugins: simpleMockPluginLibrary(),
|
|
State: state,
|
|
}
|
|
g, err := (&BasicGraphBuilder{
|
|
Steps: cbdTestSteps(applyBuilder.Steps()),
|
|
Name: "ApplyGraphBuilder",
|
|
}).Build(addrs.RootModuleInstance)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
return filterInstances(g)
|
|
}
|
|
|
|
// override the apply graph builder to halt the process after CBD
|
|
func cbdTestSteps(steps []GraphTransformer) []GraphTransformer {
|
|
found := false
|
|
var i int
|
|
var t GraphTransformer
|
|
for i, t = range steps {
|
|
if _, ok := t.(*CBDEdgeTransformer); ok {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
panic("CBDEdgeTransformer not found")
|
|
}
|
|
|
|
return steps[:i+1]
|
|
}
|
|
|
|
// remove extra nodes for easier test comparisons
|
|
func filterInstances(g *Graph) *Graph {
|
|
for _, v := range g.Vertices() {
|
|
if _, ok := v.(GraphNodeResourceInstance); !ok {
|
|
g.Remove(v)
|
|
}
|
|
|
|
}
|
|
return g
|
|
}
|
|
|
|
func TestCBDEdgeTransformer(t *testing.T) {
|
|
changes := &plans.Changes{
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.A"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.CreateThenDelete,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.B"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.Update,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
state := states.NewState()
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.A").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"A"}`),
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.B").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
|
|
g := cbdTestGraph(t, "transform-destroy-cbd-edge-basic", changes, state)
|
|
g = filterInstances(g)
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
expected := regexp.MustCompile(strings.TrimSpace(`
|
|
(?m)test_object.A
|
|
test_object.A \(destroy deposed \w+\)
|
|
test_object.A
|
|
test_object.B
|
|
test_object.B
|
|
test_object.A
|
|
`))
|
|
|
|
if !expected.MatchString(actual) {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestCBDEdgeTransformerMulti(t *testing.T) {
|
|
changes := &plans.Changes{
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.A"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.CreateThenDelete,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.B"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.CreateThenDelete,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.C"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.Update,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
state := states.NewState()
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.A").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"A"}`),
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.B").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"B"}`),
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.C").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"C","test_list":["x"]}`),
|
|
Dependencies: []addrs.ConfigResource{
|
|
mustConfigResourceAddr("test_object.A"),
|
|
mustConfigResourceAddr("test_object.B"),
|
|
},
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
|
|
g := cbdTestGraph(t, "transform-destroy-cbd-edge-multi", changes, state)
|
|
g = filterInstances(g)
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
expected := regexp.MustCompile(strings.TrimSpace(`
|
|
(?m)test_object.A
|
|
test_object.A \(destroy deposed \w+\)
|
|
test_object.A
|
|
test_object.C
|
|
test_object.B
|
|
test_object.B \(destroy deposed \w+\)
|
|
test_object.B
|
|
test_object.C
|
|
test_object.C
|
|
test_object.A
|
|
test_object.B
|
|
`))
|
|
|
|
if !expected.MatchString(actual) {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
|
|
changes := &plans.Changes{
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.A"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.CreateThenDelete,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.B[0]"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.Update,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.B[1]"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.Update,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
state := states.NewState()
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.A").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"A"}`),
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.B[0]").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.B[1]").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
|
|
g := cbdTestGraph(t, "transform-cbd-destroy-edge-count", changes, state)
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
expected := regexp.MustCompile(strings.TrimSpace(`
|
|
(?m)test_object.A
|
|
test_object.A \(destroy deposed \w+\)
|
|
test_object.A
|
|
test_object.B\[0\]
|
|
test_object.B\[1\]
|
|
test_object.B\[0\]
|
|
test_object.A
|
|
test_object.B\[1\]
|
|
test_object.A`))
|
|
|
|
if !expected.MatchString(actual) {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
|
|
changes := &plans.Changes{
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.A[0]"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.CreateThenDelete,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.A[1]"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.CreateThenDelete,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.B[0]"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.Update,
|
|
},
|
|
},
|
|
{
|
|
Addr: mustResourceInstanceAddr("test_object.B[1]"),
|
|
ChangeSrc: plans.ChangeSrc{
|
|
Action: plans.Update,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
state := states.NewState()
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.A[0]").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"A"}`),
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.A[1]").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"A"}`),
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.B[0]").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
root.SetResourceInstanceCurrent(
|
|
mustResourceInstanceAddr("test_object.B[1]").Resource,
|
|
&states.ResourceInstanceObjectSrc{
|
|
Status: states.ObjectReady,
|
|
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
|
|
},
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
)
|
|
|
|
g := cbdTestGraph(t, "transform-cbd-destroy-edge-both-count", changes, state)
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
expected := regexp.MustCompile(strings.TrimSpace(`
|
|
test_object.A\[0\]
|
|
test_object.A\[0\] \(destroy deposed \w+\)
|
|
test_object.A\[0\]
|
|
test_object.B\[0\]
|
|
test_object.B\[1\]
|
|
test_object.A\[1\]
|
|
test_object.A\[1\] \(destroy deposed \w+\)
|
|
test_object.A\[1\]
|
|
test_object.B\[0\]
|
|
test_object.B\[1\]
|
|
test_object.B\[0\]
|
|
test_object.A\[0\]
|
|
test_object.A\[1\]
|
|
test_object.B\[1\]
|
|
test_object.A\[0\]
|
|
test_object.A\[1\]
|
|
`))
|
|
|
|
if !expected.MatchString(actual) {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
}
|