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/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, 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/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 } 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 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 {