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.
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")

View File

@ -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
}