mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
f915c5d957
Due to the fact that resources can transition between each modes, trying to track the mode for a resource as a whole in state doesn't work, because there may be instances with a mode different from the resource as a whole. This is difficult for core to track, as this metadata being changed as a side effect from multiple places often causes core to see the incorrect mode when evaluating instances. Since core can always determine the correct mode to evaluate from the configuration, we don't need to interrogate the state to know the mode. Once core no longer needs to reference EachMode from states, the resource state can simply be a container for instances, and doesn't need to try and track the "current" mode.
259 lines
7.2 KiB
Go
259 lines
7.2 KiB
Go
package states
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/go-test/deep"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
)
|
|
|
|
func TestState(t *testing.T) {
|
|
// This basic tests exercises the main mutation methods to construct
|
|
// a state. It is not fully comprehensive, so other tests should visit
|
|
// more esoteric codepaths.
|
|
|
|
state := NewState()
|
|
|
|
rootModule := state.RootModule()
|
|
if rootModule == nil {
|
|
t.Errorf("root module is nil; want valid object")
|
|
}
|
|
|
|
rootModule.SetLocalValue("foo", cty.StringVal("foo value"))
|
|
rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false)
|
|
rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true)
|
|
rootModule.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_thing",
|
|
Name: "baz",
|
|
}.Instance(addrs.IntKey(0)),
|
|
&ResourceInstanceObjectSrc{
|
|
Status: ObjectReady,
|
|
SchemaVersion: 1,
|
|
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
|
|
childModule := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
|
|
childModule.SetOutputValue("pizza", cty.StringVal("hawaiian"), false)
|
|
multiModA := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")))
|
|
multiModA.SetOutputValue("pizza", cty.StringVal("cheese"), false)
|
|
multiModB := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")))
|
|
multiModB.SetOutputValue("pizza", cty.StringVal("sausage"), false)
|
|
|
|
want := &State{
|
|
Modules: map[string]*Module{
|
|
"": {
|
|
Addr: addrs.RootModuleInstance,
|
|
LocalValues: map[string]cty.Value{
|
|
"foo": cty.StringVal("foo value"),
|
|
},
|
|
OutputValues: map[string]*OutputValue{
|
|
"bar": {
|
|
Addr: addrs.AbsOutputValue{
|
|
OutputValue: addrs.OutputValue{
|
|
Name: "bar",
|
|
},
|
|
},
|
|
Value: cty.StringVal("bar value"),
|
|
Sensitive: false,
|
|
},
|
|
"secret": {
|
|
Addr: addrs.AbsOutputValue{
|
|
OutputValue: addrs.OutputValue{
|
|
Name: "secret",
|
|
},
|
|
},
|
|
Value: cty.StringVal("secret value"),
|
|
Sensitive: true,
|
|
},
|
|
},
|
|
Resources: map[string]*Resource{
|
|
"test_thing.baz": {
|
|
Addr: addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_thing",
|
|
Name: "baz",
|
|
}.Absolute(addrs.RootModuleInstance),
|
|
|
|
Instances: map[addrs.InstanceKey]*ResourceInstance{
|
|
addrs.IntKey(0): {
|
|
Current: &ResourceInstanceObjectSrc{
|
|
SchemaVersion: 1,
|
|
Status: ObjectReady,
|
|
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
|
},
|
|
Deposed: map[DeposedKey]*ResourceInstanceObjectSrc{},
|
|
},
|
|
},
|
|
ProviderConfig: addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"module.child": {
|
|
Addr: addrs.RootModuleInstance.Child("child", addrs.NoKey),
|
|
LocalValues: map[string]cty.Value{},
|
|
OutputValues: map[string]*OutputValue{
|
|
"pizza": {
|
|
Addr: addrs.AbsOutputValue{
|
|
Module: addrs.RootModuleInstance.Child("child", addrs.NoKey),
|
|
OutputValue: addrs.OutputValue{
|
|
Name: "pizza",
|
|
},
|
|
},
|
|
Value: cty.StringVal("hawaiian"),
|
|
Sensitive: false,
|
|
},
|
|
},
|
|
Resources: map[string]*Resource{},
|
|
},
|
|
`module.multi["a"]`: {
|
|
Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")),
|
|
LocalValues: map[string]cty.Value{},
|
|
OutputValues: map[string]*OutputValue{
|
|
"pizza": {
|
|
Addr: addrs.AbsOutputValue{
|
|
Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")),
|
|
OutputValue: addrs.OutputValue{
|
|
Name: "pizza",
|
|
},
|
|
},
|
|
Value: cty.StringVal("cheese"),
|
|
Sensitive: false,
|
|
},
|
|
},
|
|
Resources: map[string]*Resource{},
|
|
},
|
|
`module.multi["b"]`: {
|
|
Addr: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")),
|
|
LocalValues: map[string]cty.Value{},
|
|
OutputValues: map[string]*OutputValue{
|
|
"pizza": {
|
|
Addr: addrs.AbsOutputValue{
|
|
Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")),
|
|
OutputValue: addrs.OutputValue{
|
|
Name: "pizza",
|
|
},
|
|
},
|
|
Value: cty.StringVal("sausage"),
|
|
Sensitive: false,
|
|
},
|
|
},
|
|
Resources: map[string]*Resource{},
|
|
},
|
|
},
|
|
}
|
|
|
|
{
|
|
// Our structure goes deep, so we need to temporarily override the
|
|
// deep package settings to ensure that we visit the full structure.
|
|
oldDeepDepth := deep.MaxDepth
|
|
oldDeepCompareUnexp := deep.CompareUnexportedFields
|
|
deep.MaxDepth = 50
|
|
deep.CompareUnexportedFields = true
|
|
defer func() {
|
|
deep.MaxDepth = oldDeepDepth
|
|
deep.CompareUnexportedFields = oldDeepCompareUnexp
|
|
}()
|
|
}
|
|
|
|
for _, problem := range deep.Equal(state, want) {
|
|
t.Error(problem)
|
|
}
|
|
|
|
expectedOutputs := map[string]string{
|
|
`module.multi["a"].output.pizza`: "cheese",
|
|
`module.multi["b"].output.pizza`: "sausage",
|
|
}
|
|
|
|
for _, o := range state.ModuleOutputs(addrs.RootModuleInstance, addrs.ModuleCall{Name: "multi"}) {
|
|
addr := o.Addr.String()
|
|
expected := expectedOutputs[addr]
|
|
delete(expectedOutputs, addr)
|
|
|
|
if expected != o.Value.AsString() {
|
|
t.Fatalf("expected %q:%q, got %q", addr, expected, o.Value.AsString())
|
|
}
|
|
}
|
|
|
|
for addr, o := range expectedOutputs {
|
|
t.Fatalf("missing output %q:%q", addr, o)
|
|
}
|
|
}
|
|
|
|
func TestStateDeepCopy(t *testing.T) {
|
|
state := NewState()
|
|
|
|
rootModule := state.RootModule()
|
|
if rootModule == nil {
|
|
t.Errorf("root module is nil; want valid object")
|
|
}
|
|
|
|
rootModule.SetLocalValue("foo", cty.StringVal("foo value"))
|
|
rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false)
|
|
rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true)
|
|
rootModule.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_thing",
|
|
Name: "baz",
|
|
}.Instance(addrs.IntKey(0)),
|
|
&ResourceInstanceObjectSrc{
|
|
Status: ObjectReady,
|
|
SchemaVersion: 1,
|
|
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
|
Private: []byte("private data"),
|
|
Dependencies: []addrs.ConfigResource{},
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
rootModule.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_thing",
|
|
Name: "bar",
|
|
}.Instance(addrs.IntKey(0)),
|
|
&ResourceInstanceObjectSrc{
|
|
Status: ObjectReady,
|
|
SchemaVersion: 1,
|
|
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
|
Private: []byte("private data"),
|
|
Dependencies: []addrs.ConfigResource{
|
|
{
|
|
Module: addrs.RootModule,
|
|
Resource: addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_thing",
|
|
Name: "baz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
|
|
childModule := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
|
|
childModule.SetOutputValue("pizza", cty.StringVal("hawaiian"), false)
|
|
|
|
stateCopy := state.DeepCopy()
|
|
if !state.Equal(stateCopy) {
|
|
t.Fatalf("\nexpected:\n%q\ngot:\n%q\n", state, stateCopy)
|
|
}
|
|
}
|