command/diff: Small additional context about deposed objects

In the very unusual situation where we end up planning to destroy a
deposed object alone, it's likely that we're exposing users to this idea
of "deposed" for the very first time.

This additional sentence will hopefully give some extra context for what
that means. We don't really have room here for a lengthy explanation about
how deposed objects come to exist but this will hopefully be enough of
a hook to help users connect this with an error they saw on a previous
run, or at least to have some additional keywords to search for if they
want to research further.
This commit is contained in:
Martin Atkins 2021-05-12 15:40:55 -07:00
parent f2adfb6e2a
commit 917309fb5a
3 changed files with 31 additions and 3 deletions

View File

@ -427,6 +427,7 @@ Terraform will perform the following actions:
} }
# test_instance.foo (deposed object 00000000) will be destroyed # test_instance.foo (deposed object 00000000) will be destroyed
# (left over from a partially-failed replacement of this instance)
- resource "test_instance" "foo" { - resource "test_instance" "foo" {
- ami = "bar" -> null - ami = "bar" -> null

View File

@ -67,6 +67,10 @@ func ResourceChange(
} }
case plans.Delete: case plans.Delete:
buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be [bold][red]destroyed", dispAddr))) buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be [bold][red]destroyed", dispAddr)))
if change.DeposedKey != states.NotDeposed {
// Some extra context about this unusual situation.
buf.WriteString(color.Color(fmt.Sprint("\n # (left over from a partially-failed replacement of this instance)")))
}
default: default:
// should never happen, since the above is exhaustive // should never happen, since the above is exhaustive
buf.WriteString(fmt.Sprintf("%s has an action the plan renderer doesn't support (this is a bug)", dispAddr)) buf.WriteString(fmt.Sprintf("%s has an action the plan renderer doesn't support (this is a bug)", dispAddr))

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/states"
"github.com/mitchellh/colorstring" "github.com/mitchellh/colorstring"
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
) )
@ -88,6 +89,27 @@ func TestResourceChange_primitiveTypes(t *testing.T) {
- resource "test_instance" "example" { - resource "test_instance" "example" {
- id = "i-02ae66f368e8518a9" -> null - id = "i-02ae66f368e8518a9" -> null
} }
`,
},
"deletion of deposed object": {
Action: plans.Delete,
Mode: addrs.ManagedResourceMode,
DeposedKey: states.DeposedKey("byebye"),
Before: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("i-02ae66f368e8518a9"),
}),
After: cty.NullVal(cty.EmptyObject),
Schema: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Computed: true},
},
},
RequiredReplace: cty.NewPathSet(),
ExpectedOutput: ` # test_instance.example (deposed object byebye) will be destroyed
# (left over from a partially-failed replacement of this instance)
- resource "test_instance" "example" {
- id = "i-02ae66f368e8518a9" -> null
}
`, `,
}, },
"deletion (empty string)": { "deletion (empty string)": {
@ -4081,6 +4103,7 @@ type testCase struct {
Action plans.Action Action plans.Action
ActionReason plans.ResourceInstanceChangeActionReason ActionReason plans.ResourceInstanceChangeActionReason
Mode addrs.ResourceMode Mode addrs.ResourceMode
DeposedKey states.DeposedKey
Before cty.Value Before cty.Value
BeforeValMarks []cty.PathValueMarks BeforeValMarks []cty.PathValueMarks
AfterValMarks []cty.PathValueMarks AfterValMarks []cty.PathValueMarks
@ -4127,6 +4150,7 @@ func runTestCases(t *testing.T, testCases map[string]testCase) {
Type: "test_instance", Type: "test_instance",
Name: "example", Name: "example",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
DeposedKey: tc.DeposedKey,
ProviderAddr: addrs.AbsProviderConfig{ ProviderAddr: addrs.AbsProviderConfig{
Provider: addrs.NewDefaultProvider("test"), Provider: addrs.NewDefaultProvider("test"),
Module: addrs.RootModule, Module: addrs.RootModule,
@ -4143,9 +4167,8 @@ func runTestCases(t *testing.T, testCases map[string]testCase) {
} }
output := ResourceChange(change, tc.Schema, color) output := ResourceChange(change, tc.Schema, color)
if output != tc.ExpectedOutput { if diff := cmp.Diff(output, tc.ExpectedOutput); diff != "" {
t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.ExpectedOutput) t.Errorf("wrong output\n%s", diff)
t.Errorf("%s", cmp.Diff(output, tc.ExpectedOutput))
} }
}) })
} }