From 345dfaccb6ae6f05e55a50aadbfea649d2aab0ee Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 19 Jul 2019 16:39:16 -0400 Subject: [PATCH] Account for deposed instances in show command, adding the details for each deposed instance. Prevent crash if the current instance is missing. --- command/format/state.go | 143 +++++++++++++++++++++-------------- command/format/state_test.go | 119 +++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 55 deletions(-) diff --git a/command/format/state.go b/command/format/state.go index 0130f5cf1..be1ea24de 100644 --- a/command/format/state.go +++ b/command/format/state.go @@ -98,78 +98,111 @@ func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraf // Go through each resource and begin building up the output. for _, key := range names { for k, v := range m.Resources[key].Instances { + // keep these in order to keep the current object first, and + // provide deterministic output for the deposed objects + type obj struct { + header string + instance *states.ResourceInstanceObjectSrc + } + instances := []obj{} + addr := m.Resources[key].Addr taintStr := "" - if v.Current.Status == 'T' { + if v.Current != nil && v.Current.Status == 'T' { taintStr = " (tainted)" } - p.buf.WriteString(fmt.Sprintf("# %s:%s\n", addr.Absolute(m.Addr).Instance(k), taintStr)) - var schema *configschema.Block - provider := m.Resources[key].ProviderConfig.ProviderConfig.StringCompact() - if _, exists := schemas.Providers[provider]; !exists { - // This should never happen in normal use because we should've - // loaded all of the schemas and checked things prior to this - // point. We can't return errors here, but since this is UI code - // we will try to do _something_ reasonable. - p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider)) - continue + instances = append(instances, + obj{fmt.Sprintf("# %s:%s\n", addr.Absolute(m.Addr).Instance(k), taintStr), v.Current}) + + for dk, v := range v.Deposed { + instances = append(instances, + obj{fmt.Sprintf("# %s: (deposed object %s)\n", addr.Absolute(m.Addr).Instance(k), dk), v}) } - switch addr.Mode { - case addrs.ManagedResourceMode: - schema, _ = schemas.ResourceTypeConfig( - provider, - addr.Mode, - addr.Type, - ) - if schema == nil { - p.buf.WriteString(fmt.Sprintf( - "# missing schema for provider %q resource type %s\n\n", provider, addr.Type)) + // Sort the instances for consistent output. + // Starting the sort from the second index, so the current instance + // is always first. + sort.Slice(instances[1:], func(i, j int) bool { + return instances[i+1].header < instances[j+1].header + }) + + for _, obj := range instances { + header := obj.header + instance := obj.instance + p.buf.WriteString(header) + if instance == nil { + // this shouldn't happen, but there's nothing to do here so + // don't panic below. continue } - p.buf.WriteString(fmt.Sprintf( - "resource %q %q {", - addr.Type, - addr.Name, - )) - case addrs.DataResourceMode: - schema, _ = schemas.ResourceTypeConfig( - provider, - addr.Mode, - addr.Type, - ) - if schema == nil { - p.buf.WriteString(fmt.Sprintf( - "# missing schema for provider %q data source %s\n\n", provider, addr.Type)) + var schema *configschema.Block + provider := m.Resources[key].ProviderConfig.ProviderConfig.StringCompact() + if _, exists := schemas.Providers[provider]; !exists { + // This should never happen in normal use because we should've + // loaded all of the schemas and checked things prior to this + // point. We can't return errors here, but since this is UI code + // we will try to do _something_ reasonable. + p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider)) continue } - p.buf.WriteString(fmt.Sprintf( - "data %q %q {", - addr.Type, - addr.Name, - )) - default: - // should never happen, since the above is exhaustive - p.buf.WriteString(addr.String()) - } + switch addr.Mode { + case addrs.ManagedResourceMode: + schema, _ = schemas.ResourceTypeConfig( + provider, + addr.Mode, + addr.Type, + ) + if schema == nil { + p.buf.WriteString(fmt.Sprintf( + "# missing schema for provider %q resource type %s\n\n", provider, addr.Type)) + continue + } - val, err := v.Current.Decode(schema.ImpliedType()) - if err != nil { - fmt.Println(err.Error()) - break - } + p.buf.WriteString(fmt.Sprintf( + "resource %q %q {", + addr.Type, + addr.Name, + )) + case addrs.DataResourceMode: + schema, _ = schemas.ResourceTypeConfig( + provider, + addr.Mode, + addr.Type, + ) + if schema == nil { + p.buf.WriteString(fmt.Sprintf( + "# missing schema for provider %q data source %s\n\n", provider, addr.Type)) + continue + } - path := make(cty.Path, 0, 3) - bodyWritten := p.writeBlockBodyDiff(schema, val.Value, val.Value, 2, path) - if bodyWritten { - p.buf.WriteString("\n") - } + p.buf.WriteString(fmt.Sprintf( + "data %q %q {", + addr.Type, + addr.Name, + )) + default: + // should never happen, since the above is exhaustive + p.buf.WriteString(addr.String()) + } - p.buf.WriteString("}\n\n") + val, err := instance.Decode(schema.ImpliedType()) + if err != nil { + fmt.Println(err.Error()) + break + } + + path := make(cty.Path, 0, 3) + bodyWritten := p.writeBlockBodyDiff(schema, val.Value, val.Value, 2, path) + if bodyWritten { + p.buf.WriteString("\n") + } + + p.buf.WriteString("}\n\n") + } } } p.buf.WriteString("\n") diff --git a/command/format/state_test.go b/command/format/state_test.go index 5bd6ea34c..2b00da9b8 100644 --- a/command/format/state_test.go +++ b/command/format/state_test.go @@ -47,6 +47,22 @@ func TestState(t *testing.T) { }, nestedStateOutput, }, + { + &StateOpts{ + State: deposedState(t), + Color: disabledColorize, + Schemas: testSchemas(), + }, + deposedNestedStateOutput, + }, + { + &StateOpts{ + State: onlyDeposedState(t), + Color: disabledColorize, + Schemas: testSchemas(), + }, + onlyDeposedOutput, + }, { &StateOpts{ State: stateWithMoreOutputs(t), @@ -152,6 +168,43 @@ resource "test_resource" "baz" { } }` +const deposedNestedStateOutput = `# test_resource.baz[0]: +resource "test_resource" "baz" { + woozles = "confuzles" + + nested { + value = "42" + } +} + +# test_resource.baz[0]: (deposed object 1234) +resource "test_resource" "baz" { + woozles = "confuzles" + + nested { + value = "42" + } +}` + +const onlyDeposedOutput = `# test_resource.baz[0]: +# test_resource.baz[0]: (deposed object 1234) +resource "test_resource" "baz" { + woozles = "confuzles" + + nested { + value = "42" + } +} + +# test_resource.baz[0]: (deposed object 5678) +resource "test_resource" "baz" { + woozles = "confuzles" + + nested { + value = "42" + } +}` + const stateWithMoreOutputsOutput = `# test_resource.baz[0]: resource "test_resource" "baz" { woozles = "confuzles" @@ -272,3 +325,69 @@ func nestedState(t *testing.T) *states.State { ) return state } + +func deposedState(t *testing.T) *states.State { + state := nestedState(t) + rootModule := state.RootModule() + rootModule.SetResourceInstanceDeposed( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "baz", + }.Instance(addrs.IntKey(0)), + states.DeposedKey("1234"), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + SchemaVersion: 1, + AttrsJSON: []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`), + }, + addrs.ProviderConfig{ + Type: "test", + }.Absolute(addrs.RootModuleInstance), + ) + return state +} + +// replicate a corrupt resource where only a deposed exists +func onlyDeposedState(t *testing.T) *states.State { + state := states.NewState() + + rootModule := state.RootModule() + if rootModule == nil { + t.Errorf("root module is nil; want valid object") + } + + rootModule.SetResourceInstanceDeposed( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "baz", + }.Instance(addrs.IntKey(0)), + states.DeposedKey("1234"), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + SchemaVersion: 1, + AttrsJSON: []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`), + }, + addrs.ProviderConfig{ + Type: "test", + }.Absolute(addrs.RootModuleInstance), + ) + rootModule.SetResourceInstanceDeposed( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "baz", + }.Instance(addrs.IntKey(0)), + states.DeposedKey("5678"), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + SchemaVersion: 1, + AttrsJSON: []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`), + }, + addrs.ProviderConfig{ + Type: "test", + }.Absolute(addrs.RootModuleInstance), + ) + return state +}