From f915c5d9570b746919eda6104b34515e090abd59 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 24 Apr 2020 10:33:55 -0400 Subject: [PATCH 1/4] remove EachMode from resource state 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. --- states/eachmode_string.go | 35 -------------------------------- states/module.go | 25 ++++++++++------------- states/resource.go | 31 ---------------------------- states/state_deepcopy.go | 1 - states/state_test.go | 1 - states/statefile/version4.go | 39 ++---------------------------------- states/sync.go | 4 ++-- 7 files changed, 15 insertions(+), 121 deletions(-) delete mode 100644 states/eachmode_string.go diff --git a/states/eachmode_string.go b/states/eachmode_string.go deleted file mode 100644 index 0dc73499a..000000000 --- a/states/eachmode_string.go +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by "stringer -type EachMode"; DO NOT EDIT. - -package states - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[NoEach-0] - _ = x[EachList-76] - _ = x[EachMap-77] -} - -const ( - _EachMode_name_0 = "NoEach" - _EachMode_name_1 = "EachListEachMap" -) - -var ( - _EachMode_index_1 = [...]uint8{0, 8, 15} -) - -func (i EachMode) String() string { - switch { - case i == 0: - return _EachMode_name_0 - case 76 <= i && i <= 77: - i -= 76 - return _EachMode_name_1[_EachMode_index_1[i]:_EachMode_index_1[i+1]] - default: - return "EachMode(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/states/module.go b/states/module.go index ddfada9ae..760625e05 100644 --- a/states/module.go +++ b/states/module.go @@ -51,10 +51,10 @@ func (ms *Module) ResourceInstance(addr addrs.ResourceInstance) *ResourceInstanc return rs.Instance(addr.Key) } -// SetResourceMeta updates the resource-level metadata for the resource +// SetResourceProvider updates the resource-level metadata for the resource // with the given address, creating the resource state for it if it doesn't // already exist. -func (ms *Module) SetResourceMeta(addr addrs.Resource, eachMode EachMode, provider addrs.AbsProviderConfig) { +func (ms *Module) SetResourceProvider(addr addrs.Resource, provider addrs.AbsProviderConfig) { rs := ms.Resource(addr) if rs == nil { rs = &Resource{ @@ -64,7 +64,6 @@ func (ms *Module) SetResourceMeta(addr addrs.Resource, eachMode EachMode, provid ms.Resources[addr.String()] = rs } - rs.EachMode = eachMode rs.ProviderConfig = provider } @@ -97,9 +96,7 @@ func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *R if obj == nil && rs != nil { // does the resource have any other objects? // if not then delete the whole resource - // When deleting the resource, ensure that its EachMode is NoEach, - // as a resource with EachList or EachMap can have 0 instances and be valid - if rs.EachMode == NoEach && len(rs.Instances) == 0 { + if len(rs.Instances) == 0 { delete(ms.Resources, addr.Resource.String()) return } @@ -115,7 +112,7 @@ func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *R // If we have no objects at all then we'll clean up. delete(rs.Instances, addr.Key) // Delete the resource if it has no instances, but only if NoEach - if rs.EachMode == NoEach && len(rs.Instances) == 0 { + if len(rs.Instances) == 0 { delete(ms.Resources, addr.Resource.String()) return } @@ -125,7 +122,7 @@ func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *R } if rs == nil && obj != nil { // We don't have have a resource so make one, which is a side effect of setResourceMeta - ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) + ms.SetResourceProvider(addr.Resource, provider) // now we have a resource! so update the rs value to point to it rs = ms.Resource(addr.Resource) } @@ -134,8 +131,8 @@ func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *R if is == nil { // if we don't have a resource, create one and add to the instances is = rs.CreateInstance(addr.Key) - // update the resource meta because we have a new instance, so EachMode may have changed - ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) + // update the resource meta because we have a new + ms.SetResourceProvider(addr.Resource, provider) } // Update the resource's ProviderConfig, in case the provider has updated rs.ProviderConfig = provider @@ -159,7 +156,7 @@ func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *R // the instance is left with no objects after this operation then it will // be removed from its containing resource altogether. func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { - ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) + ms.SetResourceProvider(addr.Resource, provider) rs := ms.Resource(addr.Resource) is := rs.EnsureInstance(addr.Key) @@ -173,7 +170,7 @@ func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key De // If we have no objects at all then we'll clean up. delete(rs.Instances, addr.Key) } - if rs.EachMode == NoEach && len(rs.Instances) == 0 { + if len(rs.Instances) == 0 { // Also clean up if we only expect to have one instance anyway // and there are none. We leave the resource behind if an each mode // is active because an empty list or map of instances is a valid state. @@ -190,7 +187,7 @@ func (ms *Module) ForgetResourceInstanceAll(addr addrs.ResourceInstance) { } delete(rs.Instances, addr.Key) - if rs.EachMode == NoEach && len(rs.Instances) == 0 { + if len(rs.Instances) == 0 { // Also clean up if we only expect to have one instance anyway // and there are none. We leave the resource behind if an each mode // is active because an empty list or map of instances is a valid state. @@ -215,7 +212,7 @@ func (ms *Module) ForgetResourceInstanceDeposed(addr addrs.ResourceInstance, key // If we have no objects at all then we'll clean up. delete(rs.Instances, addr.Key) } - if rs.EachMode == NoEach && len(rs.Instances) == 0 { + if len(rs.Instances) == 0 { // Also clean up if we only expect to have one instance anyway // and there are none. We leave the resource behind if an each mode // is active because an empty list or map of instances is a valid state. diff --git a/states/resource.go b/states/resource.go index fc9f0b49a..0b6a45092 100644 --- a/states/resource.go +++ b/states/resource.go @@ -14,12 +14,6 @@ type Resource struct { // belongs to. Addr addrs.AbsResource - // EachMode is the multi-instance mode currently in use for this resource, - // or NoEach if this is a single-instance resource. This dictates what - // type of value is returned when accessing this resource via expressions - // in the Terraform language. - EachMode EachMode - // Instances contains the potentially-multiple instances associated with // this resource. This map can contain a mixture of different key types, // but only the ones of InstanceKeyType are considered current. @@ -173,31 +167,6 @@ func (i *ResourceInstance) findUnusedDeposedKey() DeposedKey { } } -// EachMode specifies the multi-instance mode for a resource. -type EachMode rune - -const ( - NoEach EachMode = 0 - EachList EachMode = 'L' - EachMap EachMode = 'M' -) - -//go:generate go run golang.org/x/tools/cmd/stringer -type EachMode - -func eachModeForInstanceKey(key addrs.InstanceKey) EachMode { - switch key.(type) { - case addrs.IntKey: - return EachList - case addrs.StringKey: - return EachMap - default: - if key == addrs.NoKey { - return NoEach - } - panic(fmt.Sprintf("don't know an each mode for instance key %#v", key)) - } -} - // DeposedKey is a 8-character hex string used to uniquely identify deposed // instance objects in the state. type DeposedKey string diff --git a/states/state_deepcopy.go b/states/state_deepcopy.go index 817e1c19d..5cd7708e1 100644 --- a/states/state_deepcopy.go +++ b/states/state_deepcopy.go @@ -88,7 +88,6 @@ func (rs *Resource) DeepCopy() *Resource { return &Resource{ Addr: rs.Addr, - EachMode: rs.EachMode, Instances: instances, ProviderConfig: rs.ProviderConfig, // technically mutable, but immutable by convention } diff --git a/states/state_test.go b/states/state_test.go index cd57757cb..9f37ec3a1 100644 --- a/states/state_test.go +++ b/states/state_test.go @@ -83,7 +83,6 @@ func TestState(t *testing.T) { Name: "baz", }.Absolute(addrs.RootModuleInstance), - EachMode: EachList, Instances: map[addrs.InstanceKey]*ResourceInstance{ addrs.IntKey(0): { Current: &ResourceInstanceObjectSrc{ diff --git a/states/statefile/version4.go b/states/statefile/version4.go index 0cb0ae9b0..b98f90f6d 100644 --- a/states/statefile/version4.go +++ b/states/statefile/version4.go @@ -95,27 +95,10 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) { } } - var eachMode states.EachMode - switch rsV4.EachMode { - case "": - eachMode = states.NoEach - case "list": - eachMode = states.EachList - case "map": - eachMode = states.EachMap - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource metadata in state", - fmt.Sprintf("Resource %s has invalid \"each\" value %q in state.", rAddr.Absolute(moduleAddr), eachMode), - )) - continue - } - ms := state.EnsureModule(moduleAddr) // Ensure the resource container object is present in the state. - ms.SetResourceMeta(rAddr, eachMode, providerAddr) + ms.SetResourceProvider(rAddr, providerAddr) for _, isV4 := range rsV4.Instances { keyRaw := isV4.IndexKey @@ -272,7 +255,7 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) { // on the incoming objects. That behavior is useful when we're making // piecemeal updates to the state during an apply, but when we're // reading the state file we want to reflect its contents exactly. - ms.SetResourceMeta(rAddr, eachMode, providerAddr) + ms.SetResourceProvider(rAddr, providerAddr) } // The root module is special in that we persist its attributes and thus @@ -394,29 +377,11 @@ func writeStateV4(file *File, w io.Writer) tfdiags.Diagnostics { continue } - var eachMode string - switch rs.EachMode { - case states.NoEach: - eachMode = "" - case states.EachList: - eachMode = "list" - case states.EachMap: - eachMode = "map" - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize resource in state", - fmt.Sprintf("Resource %s has \"each\" mode %s, which cannot be serialized in state", resourceAddr.Absolute(moduleAddr), rs.EachMode), - )) - continue - } - sV4.Resources = append(sV4.Resources, resourceStateV4{ Module: moduleAddr.String(), Mode: mode, Type: resourceAddr.Type, Name: resourceAddr.Name, - EachMode: eachMode, ProviderConfig: rs.ProviderConfig.String(), Instances: []instanceObjectStateV4{}, }) diff --git a/states/sync.go b/states/sync.go index 4b82c69fb..d0923b150 100644 --- a/states/sync.go +++ b/states/sync.go @@ -197,12 +197,12 @@ func (s *SyncState) ResourceInstanceObject(addr addrs.AbsResourceInstance, gen G // SetResourceMeta updates the resource-level metadata for the resource at // the given address, creating the containing module state and resource state // as a side-effect if not already present. -func (s *SyncState) SetResourceMeta(addr addrs.AbsResource, eachMode EachMode, provider addrs.AbsProviderConfig) { +func (s *SyncState) SetResourceProvider(addr addrs.AbsResource, provider addrs.AbsProviderConfig) { s.lock.Lock() defer s.lock.Unlock() ms := s.state.EnsureModule(addr.Module) - ms.SetResourceMeta(addr.Resource, eachMode, provider) + ms.SetResourceProvider(addr.Resource, provider) } // RemoveResource removes the entire state for the given resource, taking with From 98cf28b02d20e947a249a2a21a187fde30c15bda Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 24 Apr 2020 10:43:53 -0400 Subject: [PATCH 2/4] 2 more tests that weren't correct Found 2 more tests that still had dangling empty modules, which are now fixed. --- terraform/context_apply_test.go | 10 +++------ terraform/eval_state.go | 6 +++--- terraform/evaluate.go | 38 --------------------------------- 3 files changed, 6 insertions(+), 48 deletions(-) diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index e2add81e4..f87bb720c 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -2637,7 +2637,7 @@ func TestContext2Apply_orphanResource(t *testing.T) { Type: "test_thing", Name: "one", }.Absolute(addrs.RootModuleInstance) - s.SetResourceMeta(oneAddr, states.EachList, providerAddr) + s.SetResourceProvider(oneAddr, providerAddr) s.SetResourceInstanceCurrent(oneAddr.Instance(addrs.IntKey(0)), &states.ResourceInstanceObjectSrc{ Status: states.ObjectReady, AttrsJSON: []byte(`{}`), @@ -6111,9 +6111,7 @@ func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) { //Test that things were destroyed actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(` - -module.child: - `) +`) if actual != expected { t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) } @@ -6269,9 +6267,7 @@ func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) { //Test that things were destroyed actual := strings.TrimSpace(state.String()) expected := strings.TrimSpace(` - -module.child.child2: - `) +`) if actual != expected { t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual) } diff --git a/terraform/eval_state.go b/terraform/eval_state.go index d12adf374..c19d37801 100644 --- a/terraform/eval_state.go +++ b/terraform/eval_state.go @@ -475,7 +475,7 @@ func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) { return nil, diags.Err() } - state.SetResourceMeta(n.Addr, states.EachList, n.ProviderAddr) + state.SetResourceProvider(n.Addr, n.ProviderAddr) expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count) case n.Config.ForEach != nil: @@ -487,11 +487,11 @@ func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) { // This method takes care of all of the business logic of updating this // while ensuring that any existing instances are preserved, etc. - state.SetResourceMeta(n.Addr, states.EachMap, n.ProviderAddr) + state.SetResourceProvider(n.Addr, n.ProviderAddr) expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEach) default: - state.SetResourceMeta(n.Addr, states.NoEach, n.ProviderAddr) + state.SetResourceProvider(n.Addr, n.ProviderAddr) expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource) } diff --git a/terraform/evaluate.go b/terraform/evaluate.go index 79397f1b7..128dd7a4c 100644 --- a/terraform/evaluate.go +++ b/terraform/evaluate.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path/filepath" - "strconv" "sync" "github.com/agext/levenshtein" @@ -803,43 +802,6 @@ func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAdd return schema } -// coerceInstanceKey attempts to convert the given key to the type expected -// for the given EachMode. -// -// If the key is already of the correct type or if it cannot be converted then -// it is returned verbatim. If conversion is required and possible, the -// converted value is returned. Callers should not try to determine if -// conversion was possible, should instead just check if the result is of -// the expected type. -func (d *evaluationStateData) coerceInstanceKey(key addrs.InstanceKey, mode states.EachMode) addrs.InstanceKey { - if key == addrs.NoKey { - // An absent key can't be converted - return key - } - - switch mode { - case states.NoEach: - // No conversions possible at all - return key - case states.EachMap: - if intKey, isInt := key.(addrs.IntKey); isInt { - return addrs.StringKey(strconv.Itoa(int(intKey))) - } - return key - case states.EachList: - if strKey, isStr := key.(addrs.StringKey); isStr { - i, err := strconv.Atoi(string(strKey)) - if err != nil { - return key - } - return addrs.IntKey(i) - } - return key - default: - return key - } -} - func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics switch addr.Name { From 15a95031e5867121dcf736e64eb51506c71e6cfc Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 24 Apr 2020 21:36:44 -0400 Subject: [PATCH 3/4] remove a few traces of states.EachMode --- command/jsonstate/state.go | 5 +---- command/jsonstate/state_test.go | 5 ----- states/statemgr/testing.go | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/command/jsonstate/state.go b/command/jsonstate/state.go index ab1b29683..24eba59b5 100644 --- a/command/jsonstate/state.go +++ b/command/jsonstate/state.go @@ -259,6 +259,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module current := resource{ Address: r.Addr.Instance(k).String(), + Index: k, Type: resAddr.Type, Name: resAddr.Name, ProviderName: r.ProviderConfig.Provider.String(), @@ -276,10 +277,6 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module ) } - if r.EachMode != states.NoEach { - current.Index = k - } - schema, _ := schemas.ResourceTypeConfig( r.ProviderConfig.Provider, resAddr.Mode, diff --git a/command/jsonstate/state_test.go b/command/jsonstate/state_test.go index f5b47fe05..55d3c47f6 100644 --- a/command/jsonstate/state_test.go +++ b/command/jsonstate/state_test.go @@ -193,7 +193,6 @@ func TestMarshalResources(t *testing.T) { Name: "bar", }, }, - EachMode: states.NoEach, Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.NoKey: { Current: &states.ResourceInstanceObjectSrc{ @@ -237,7 +236,6 @@ func TestMarshalResources(t *testing.T) { Name: "bar", }, }, - EachMode: states.EachList, Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.IntKey(0): { Current: &states.ResourceInstanceObjectSrc{ @@ -281,7 +279,6 @@ func TestMarshalResources(t *testing.T) { Name: "bar", }, }, - EachMode: states.EachMap, Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.StringKey("rockhopper"): { Current: &states.ResourceInstanceObjectSrc{ @@ -325,7 +322,6 @@ func TestMarshalResources(t *testing.T) { Name: "bar", }, }, - EachMode: states.NoEach, Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.NoKey: { Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ @@ -371,7 +367,6 @@ func TestMarshalResources(t *testing.T) { Name: "bar", }, }, - EachMode: states.NoEach, Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.NoKey: { Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ diff --git a/states/statemgr/testing.go b/states/statemgr/testing.go index d36c35b99..5ebc40f2f 100644 --- a/states/statemgr/testing.go +++ b/states/statemgr/testing.go @@ -154,6 +154,6 @@ func TestFullInitialState() *states.State { Provider: addrs.NewLegacyProvider(rAddr.ImpliedProvider()), Module: addrs.RootModule, } - childMod.SetResourceMeta(rAddr, states.EachList, providerAddr) + childMod.SetResourceProvider(rAddr, providerAddr) return state } From 2bfaddcf5776343957d08aa84747c685c82d31bc Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 24 Apr 2020 21:52:05 -0400 Subject: [PATCH 4/4] fix state mv to work without EachMode The only situation where `state mv` needs to understand the each mode is when with resource addresses that may reference a single instance, or a group of for_each or count instances. In this case we can differentiate the two by checking the existence of the NoKey instance key. --- command/state_mv.go | 32 +++++++------------------------- command/state_mv_test.go | 24 +++++++++++++----------- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/command/state_mv.go b/command/state_mv.go index 7bcfb4428..10e9a86f7 100644 --- a/command/state_mv.go +++ b/command/state_mv.go @@ -279,10 +279,6 @@ func (c *StateMvCommand) Run(args []string) int { ssFrom.ForgetResourceInstanceAll(addrFrom) ssFrom.RemoveResourceIfEmpty(fromResourceAddr) - // since this is moving an instance, we can infer the target - // mode from the address. - toEachMode := eachModeForInstanceKey(addrTo.Resource.Key) - rs := stateTo.Resource(addrTo.ContainingResource()) if rs == nil { // If we're moving to an address without an index then that @@ -291,15 +287,13 @@ func (c *StateMvCommand) Run(args []string) int { // address covers both). If there's an index in the // target then allow creating the new instance here. resourceAddr := addrTo.ContainingResource() - stateTo.SyncWrapper().SetResourceMeta( + stateTo.SyncWrapper().SetResourceProvider( resourceAddr, - toEachMode, fromProviderAddr, // in this case, we bring the provider along as if we were moving the whole resource ) rs = stateTo.Resource(resourceAddr) } - rs.EachMode = toEachMode rs.Instances[addrTo.Resource.Key] = is } default: @@ -372,20 +366,6 @@ func (c *StateMvCommand) Run(args []string) int { return 0 } -func eachModeForInstanceKey(key addrs.InstanceKey) states.EachMode { - switch key.(type) { - case addrs.IntKey: - return states.EachList - case addrs.StringKey: - return states.EachMap - default: - if key == addrs.NoKey { - return states.NoEach - } - panic(fmt.Sprintf("don't know an each mode for instance key %#v", key)) - } -} - // sourceObjectAddrs takes a single source object address and expands it to // potentially multiple objects that need to be handled within it. // @@ -415,10 +395,12 @@ func (c *StateMvCommand) sourceObjectAddrs(state *states.State, matched addrs.Ta // terraform state mv aws_instance.foo aws_instance.bar[1] // That wouldn't be allowed if aws_instance.foo had multiple instances // since we can't move multiple instances into one. - if rs := state.Resource(addr); rs != nil && rs.EachMode == states.NoEach { - ret = append(ret, addr.Instance(addrs.NoKey)) - } else { - ret = append(ret, addr) + if rs := state.Resource(addr); rs != nil { + if _, ok := rs.Instances[addrs.NoKey]; ok { + ret = append(ret, addr.Instance(addrs.NoKey)) + } else { + ret = append(ret, addr) + } } default: ret = append(ret, matched) diff --git a/command/state_mv_test.go b/command/state_mv_test.go index e84aca6f1..f970185b1 100644 --- a/command/state_mv_test.go +++ b/command/state_mv_test.go @@ -97,9 +97,10 @@ func TestStateMv(t *testing.T) { if diags.HasErrors() { t.Fatal(diags.Err()) } - i := s.Resource(addr) - if i.EachMode != states.EachList { - t.Fatalf("expected each mode List, got %s", i.EachMode) + for key := range s.Resource(addr).Instances { + if _, ok := key.(addrs.IntKey); !ok { + t.Fatalf("expected each mode List, got key %q", key) + } } // change from list to map @@ -118,9 +119,10 @@ func TestStateMv(t *testing.T) { if diags.HasErrors() { t.Fatal(diags.Err()) } - i = s.Resource(addr) - if i.EachMode != states.EachMap { - t.Fatalf("expected each mode Map, got %s", i.EachMode) + for key := range s.Resource(addr).Instances { + if _, ok := key.(addrs.StringKey); !ok { + t.Fatalf("expected each mode map, found key %q", key) + } } // change from from map back to single @@ -139,9 +141,10 @@ func TestStateMv(t *testing.T) { if diags.HasErrors() { t.Fatal(diags.Err()) } - i = s.Resource(addr) - if i.EachMode != states.NoEach { - t.Fatalf("expected each mode NoEach, got %s", i.EachMode) + for key := range s.Resource(addr).Instances { + if key != addrs.NoKey { + t.Fatalf("expected no each mode, found key %q", key) + } } } @@ -179,13 +182,12 @@ func TestStateMv_resourceToInstance(t *testing.T) { Module: addrs.RootModule, }, ) - s.SetResourceMeta( + s.SetResourceProvider( addrs.Resource{ Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "bar", }.Absolute(addrs.RootModuleInstance), - states.EachList, addrs.AbsProviderConfig{ Provider: addrs.NewLegacyProvider("test"), Module: addrs.RootModule,