Account for deposed instances in show command, adding the details for

each deposed instance.
Prevent crash if the current instance is missing.
This commit is contained in:
James Bardin 2019-07-19 16:39:16 -04:00
parent b117457c6a
commit 345dfaccb6
2 changed files with 207 additions and 55 deletions

View File

@ -98,78 +98,111 @@ func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraf
// Go through each resource and begin building up the output. // Go through each resource and begin building up the output.
for _, key := range names { for _, key := range names {
for k, v := range m.Resources[key].Instances { 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 addr := m.Resources[key].Addr
taintStr := "" taintStr := ""
if v.Current.Status == 'T' { if v.Current != nil && v.Current.Status == 'T' {
taintStr = " (tainted)" taintStr = " (tainted)"
} }
p.buf.WriteString(fmt.Sprintf("# %s:%s\n", addr.Absolute(m.Addr).Instance(k), taintStr))
var schema *configschema.Block instances = append(instances,
provider := m.Resources[key].ProviderConfig.ProviderConfig.StringCompact() obj{fmt.Sprintf("# %s:%s\n", addr.Absolute(m.Addr).Instance(k), taintStr), v.Current})
if _, exists := schemas.Providers[provider]; !exists {
// This should never happen in normal use because we should've for dk, v := range v.Deposed {
// loaded all of the schemas and checked things prior to this instances = append(instances,
// point. We can't return errors here, but since this is UI code obj{fmt.Sprintf("# %s: (deposed object %s)\n", addr.Absolute(m.Addr).Instance(k), dk), v})
// we will try to do _something_ reasonable.
p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider))
continue
} }
switch addr.Mode { // Sort the instances for consistent output.
case addrs.ManagedResourceMode: // Starting the sort from the second index, so the current instance
schema, _ = schemas.ResourceTypeConfig( // is always first.
provider, sort.Slice(instances[1:], func(i, j int) bool {
addr.Mode, return instances[i+1].header < instances[j+1].header
addr.Type, })
)
if schema == nil { for _, obj := range instances {
p.buf.WriteString(fmt.Sprintf( header := obj.header
"# missing schema for provider %q resource type %s\n\n", provider, addr.Type)) 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 continue
} }
p.buf.WriteString(fmt.Sprintf( var schema *configschema.Block
"resource %q %q {", provider := m.Resources[key].ProviderConfig.ProviderConfig.StringCompact()
addr.Type, if _, exists := schemas.Providers[provider]; !exists {
addr.Name, // This should never happen in normal use because we should've
)) // loaded all of the schemas and checked things prior to this
case addrs.DataResourceMode: // point. We can't return errors here, but since this is UI code
schema, _ = schemas.ResourceTypeConfig( // we will try to do _something_ reasonable.
provider, p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", 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 continue
} }
p.buf.WriteString(fmt.Sprintf( switch addr.Mode {
"data %q %q {", case addrs.ManagedResourceMode:
addr.Type, schema, _ = schemas.ResourceTypeConfig(
addr.Name, provider,
)) addr.Mode,
default: addr.Type,
// should never happen, since the above is exhaustive )
p.buf.WriteString(addr.String()) 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()) p.buf.WriteString(fmt.Sprintf(
if err != nil { "resource %q %q {",
fmt.Println(err.Error()) addr.Type,
break 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) p.buf.WriteString(fmt.Sprintf(
bodyWritten := p.writeBlockBodyDiff(schema, val.Value, val.Value, 2, path) "data %q %q {",
if bodyWritten { addr.Type,
p.buf.WriteString("\n") 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") p.buf.WriteString("\n")

View File

@ -47,6 +47,22 @@ func TestState(t *testing.T) {
}, },
nestedStateOutput, nestedStateOutput,
}, },
{
&StateOpts{
State: deposedState(t),
Color: disabledColorize,
Schemas: testSchemas(),
},
deposedNestedStateOutput,
},
{
&StateOpts{
State: onlyDeposedState(t),
Color: disabledColorize,
Schemas: testSchemas(),
},
onlyDeposedOutput,
},
{ {
&StateOpts{ &StateOpts{
State: stateWithMoreOutputs(t), 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]: const stateWithMoreOutputsOutput = `# test_resource.baz[0]:
resource "test_resource" "baz" { resource "test_resource" "baz" {
woozles = "confuzles" woozles = "confuzles"
@ -272,3 +325,69 @@ func nestedState(t *testing.T) *states.State {
) )
return 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
}