2023-05-02 10:33:06 -05:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2023-09-20 07:16:53 -05:00
|
|
|
package tofu
|
2016-11-07 15:23:06 -06:00
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"testing"
|
2018-05-04 21:24:06 -05:00
|
|
|
|
2023-09-20 06:35:35 -05:00
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
|
|
"github.com/opentofu/opentofu/internal/states"
|
2016-11-07 15:23:06 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestOrphanResourceCountTransformer(t *testing.T) {
|
2020-11-13 15:36:40 -06:00
|
|
|
state := states.NewState()
|
|
|
|
root := state.RootModule()
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.web").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
2016-11-07 15:23:06 -06:00
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
2016-11-07 15:23:06 -06:00
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
2016-11-07 15:23:06 -06:00
|
|
|
|
|
|
|
{
|
2020-11-13 15:36:40 -06:00
|
|
|
tf := &OrphanResourceInstanceCountTransformer{
|
2016-11-07 15:23:06 -06:00
|
|
|
Concrete: testOrphanResourceConcreteFunc,
|
2018-05-04 21:24:06 -05:00
|
|
|
Addr: addrs.RootModuleInstance.Resource(
|
|
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
|
|
),
|
2020-11-13 15:36:40 -06:00
|
|
|
InstanceAddrs: []addrs.AbsResourceInstance{mustResourceInstanceAddr("aws_instance.foo[0]")},
|
|
|
|
State: state,
|
2016-11-07 15:23:06 -06:00
|
|
|
}
|
|
|
|
if err := tf.Transform(&g); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
|
|
expected := strings.TrimSpace(testTransformOrphanResourceCountBasicStr)
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOrphanResourceCountTransformer_zero(t *testing.T) {
|
2020-11-13 15:36:40 -06:00
|
|
|
state := states.NewState()
|
|
|
|
root := state.RootModule()
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.web").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
2016-11-07 15:23:06 -06:00
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
2016-11-07 15:23:06 -06:00
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
2016-11-07 15:23:06 -06:00
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
2016-11-07 15:23:06 -06:00
|
|
|
|
|
|
|
{
|
2020-11-13 15:36:40 -06:00
|
|
|
tf := &OrphanResourceInstanceCountTransformer{
|
2016-11-07 15:23:06 -06:00
|
|
|
Concrete: testOrphanResourceConcreteFunc,
|
2018-05-04 21:24:06 -05:00
|
|
|
Addr: addrs.RootModuleInstance.Resource(
|
|
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
|
|
),
|
2020-11-13 15:36:40 -06:00
|
|
|
InstanceAddrs: []addrs.AbsResourceInstance{},
|
|
|
|
State: state,
|
2016-11-07 15:23:06 -06:00
|
|
|
}
|
|
|
|
if err := tf.Transform(&g); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(g.String())
|
2020-11-13 15:36:40 -06:00
|
|
|
expected := strings.TrimSpace(testTransformOrphanResourceCountZeroStr)
|
2016-11-07 15:23:06 -06:00
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOrphanResourceCountTransformer_oneIndex(t *testing.T) {
|
2020-11-13 15:36:40 -06:00
|
|
|
state := states.NewState()
|
|
|
|
root := state.RootModule()
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.web").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
2016-11-07 15:23:06 -06:00
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
2016-11-07 15:23:06 -06:00
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
2016-11-07 16:53:04 -06:00
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2020-11-13 15:36:40 -06:00
|
|
|
)
|
2016-11-07 16:53:04 -06:00
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
2016-11-07 16:53:04 -06:00
|
|
|
|
|
|
|
{
|
2020-11-13 15:36:40 -06:00
|
|
|
tf := &OrphanResourceInstanceCountTransformer{
|
2016-11-07 16:53:04 -06:00
|
|
|
Concrete: testOrphanResourceConcreteFunc,
|
2018-05-04 21:24:06 -05:00
|
|
|
Addr: addrs.RootModuleInstance.Resource(
|
|
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
|
|
),
|
2020-11-13 15:36:40 -06:00
|
|
|
InstanceAddrs: []addrs.AbsResourceInstance{mustResourceInstanceAddr("aws_instance.foo[0]")},
|
|
|
|
State: state,
|
2016-11-07 16:53:04 -06:00
|
|
|
}
|
|
|
|
if err := tf.Transform(&g); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(g.String())
|
2020-11-13 15:36:40 -06:00
|
|
|
expected := strings.TrimSpace(testTransformOrphanResourceCountOneIndexStr)
|
2016-11-07 16:53:04 -06:00
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-10 15:25:11 -06:00
|
|
|
func TestOrphanResourceCountTransformer_deposed(t *testing.T) {
|
|
|
|
state := states.NewState()
|
|
|
|
root := state.RootModule()
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.web").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2023-02-10 15:25:11 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2023-02-10 15:25:11 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2023-02-10 15:25:11 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceDeposed(
|
|
|
|
mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
|
|
|
|
states.NewDeposedKey(),
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2023-02-10 15:25:11 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
|
|
|
|
|
|
|
{
|
|
|
|
tf := &OrphanResourceInstanceCountTransformer{
|
|
|
|
Concrete: testOrphanResourceConcreteFunc,
|
|
|
|
Addr: addrs.RootModuleInstance.Resource(
|
|
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
|
|
),
|
|
|
|
InstanceAddrs: []addrs.AbsResourceInstance{mustResourceInstanceAddr("aws_instance.foo[0]")},
|
|
|
|
State: state,
|
|
|
|
}
|
|
|
|
if err := tf.Transform(&g); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
|
|
expected := strings.TrimSpace(testTransformOrphanResourceCountDeposedStr)
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 15:31:07 -05:00
|
|
|
// When converting from a NoEach mode to an EachMap via a switch to for_each,
|
|
|
|
// an edge is necessary to ensure that the map-key'd instances
|
|
|
|
// are evaluated after the NoKey resource, because the final instance evaluated
|
|
|
|
// sets the whole resource's EachMode.
|
|
|
|
func TestOrphanResourceCountTransformer_ForEachEdgesAdded(t *testing.T) {
|
|
|
|
state := states.BuildState(func(s *states.SyncState) {
|
|
|
|
// "bar" key'd resource
|
|
|
|
s.SetResourceInstanceCurrent(
|
|
|
|
addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "aws_instance",
|
|
|
|
Name: "foo",
|
|
|
|
}.Instance(addrs.StringKey("bar")).Absolute(addrs.RootModuleInstance),
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
AttrsFlat: map[string]string{
|
|
|
|
"id": "foo",
|
|
|
|
},
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2019-08-13 15:31:07 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// NoKey'd resource
|
|
|
|
s.SetResourceInstanceCurrent(
|
|
|
|
addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "aws_instance",
|
|
|
|
Name: "foo",
|
|
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
AttrsFlat: map[string]string{
|
|
|
|
"id": "foo",
|
|
|
|
},
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
},
|
2023-12-11 14:10:03 -06:00
|
|
|
mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
|
2019-08-13 15:31:07 -05:00
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
|
|
|
|
|
|
|
{
|
2020-11-13 15:36:40 -06:00
|
|
|
tf := &OrphanResourceInstanceCountTransformer{
|
2019-08-13 15:31:07 -05:00
|
|
|
Concrete: testOrphanResourceConcreteFunc,
|
|
|
|
Addr: addrs.RootModuleInstance.Resource(
|
|
|
|
addrs.ManagedResourceMode, "aws_instance", "foo",
|
|
|
|
),
|
2020-11-13 15:36:40 -06:00
|
|
|
InstanceAddrs: []addrs.AbsResourceInstance{},
|
|
|
|
State: state,
|
2019-08-13 15:31:07 -05:00
|
|
|
}
|
|
|
|
if err := tf.Transform(&g); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
|
|
expected := strings.TrimSpace(testTransformOrphanResourceForEachStr)
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad:\n\n%s", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-07 15:23:06 -06:00
|
|
|
const testTransformOrphanResourceCountBasicStr = `
|
|
|
|
aws_instance.foo[2] (orphan)
|
|
|
|
`
|
|
|
|
|
|
|
|
const testTransformOrphanResourceCountZeroStr = `
|
2020-11-13 15:36:40 -06:00
|
|
|
aws_instance.foo[0] (orphan)
|
2016-11-07 15:23:06 -06:00
|
|
|
aws_instance.foo[2] (orphan)
|
|
|
|
`
|
|
|
|
|
|
|
|
const testTransformOrphanResourceCountOneIndexStr = `
|
|
|
|
aws_instance.foo[1] (orphan)
|
|
|
|
`
|
|
|
|
|
2023-02-10 15:25:11 -06:00
|
|
|
const testTransformOrphanResourceCountDeposedStr = `
|
|
|
|
aws_instance.foo[1] (orphan)
|
|
|
|
`
|
|
|
|
|
2019-08-13 15:31:07 -05:00
|
|
|
const testTransformOrphanResourceForEachStr = `
|
|
|
|
aws_instance.foo (orphan)
|
|
|
|
aws_instance.foo["bar"] (orphan)
|
|
|
|
`
|