From 917309fb5abbb3b9132bb2dbdc2ee35f4839c46c Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 12 May 2021 15:40:55 -0700 Subject: [PATCH] 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. --- backend/local/backend_plan_test.go | 1 + command/format/diff.go | 4 ++++ command/format/diff_test.go | 29 ++++++++++++++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/backend/local/backend_plan_test.go b/backend/local/backend_plan_test.go index aba80d1b74..3b90724d88 100644 --- a/backend/local/backend_plan_test.go +++ b/backend/local/backend_plan_test.go @@ -427,6 +427,7 @@ Terraform will perform the following actions: } # test_instance.foo (deposed object 00000000) will be destroyed + # (left over from a partially-failed replacement of this instance) - resource "test_instance" "foo" { - ami = "bar" -> null diff --git a/command/format/diff.go b/command/format/diff.go index 92e271d0e9..13702415d0 100644 --- a/command/format/diff.go +++ b/command/format/diff.go @@ -67,6 +67,10 @@ func ResourceChange( } case plans.Delete: 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: // 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)) diff --git a/command/format/diff_test.go b/command/format/diff_test.go index 41c42553ed..2bd7dd8f5e 100644 --- a/command/format/diff_test.go +++ b/command/format/diff_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/states" "github.com/mitchellh/colorstring" "github.com/zclconf/go-cty/cty" ) @@ -88,6 +89,27 @@ func TestResourceChange_primitiveTypes(t *testing.T) { - resource "test_instance" "example" { - 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)": { @@ -4081,6 +4103,7 @@ type testCase struct { Action plans.Action ActionReason plans.ResourceInstanceChangeActionReason Mode addrs.ResourceMode + DeposedKey states.DeposedKey Before cty.Value BeforeValMarks []cty.PathValueMarks AfterValMarks []cty.PathValueMarks @@ -4127,6 +4150,7 @@ func runTestCases(t *testing.T, testCases map[string]testCase) { Type: "test_instance", Name: "example", }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + DeposedKey: tc.DeposedKey, ProviderAddr: addrs.AbsProviderConfig{ Provider: addrs.NewDefaultProvider("test"), Module: addrs.RootModule, @@ -4143,9 +4167,8 @@ func runTestCases(t *testing.T, testCases map[string]testCase) { } output := ResourceChange(change, tc.Schema, color) - if output != tc.ExpectedOutput { - t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.ExpectedOutput) - t.Errorf("%s", cmp.Diff(output, tc.ExpectedOutput)) + if diff := cmp.Diff(output, tc.ExpectedOutput); diff != "" { + t.Errorf("wrong output\n%s", diff) } }) }