package terraform import ( "testing" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" "github.com/zclconf/go-cty/cty" ) func TestNodePlanDeposedResourceInstanceObject_Execute(t *testing.T) { deposedKey := states.NewDeposedKey() state := states.NewState() absResource := mustResourceInstanceAddr("test_instance.foo") state.Module(addrs.RootModuleInstance).SetResourceInstanceDeposed( absResource.Resource, deposedKey, &states.ResourceInstanceObjectSrc{ Status: states.ObjectTainted, AttrsJSON: []byte(`{"id":"bar"}`), }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), ) p := testProvider("test") p.ConfigureProvider(providers.ConfigureProviderRequest{}) p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ UpgradedState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("bar"), }), } ctx := &MockEvalContext{ StateState: state.SyncWrapper(), PrevRunStateState: state.DeepCopy().SyncWrapper(), RefreshStateState: state.DeepCopy().SyncWrapper(), ProviderProvider: p, ProviderSchemaSchema: &ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_instance": { Attributes: map[string]*configschema.Attribute{ "id": { Type: cty.String, Computed: true, }, }, }, }, }, ChangesChanges: plans.NewChanges().SyncWrapper(), } node := NodePlanDeposedResourceInstanceObject{ NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ Addr: absResource, NodeAbstractResource: NodeAbstractResource{ ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), }, }, DeposedKey: deposedKey, } err := node.Execute(ctx, walkPlan) if err != nil { t.Fatalf("unexpected error: %s", err) } if !p.UpgradeResourceStateCalled { t.Errorf("UpgradeResourceState wasn't called; should've been called to upgrade the previous run's object") } if !p.ReadResourceCalled { t.Errorf("ReadResource wasn't called; should've been called to refresh the deposed object") } change := ctx.Changes().GetResourceInstanceChange(absResource, deposedKey) if got, want := change.ChangeSrc.Action, plans.Delete; got != want { t.Fatalf("wrong planned action\ngot: %s\nwant: %s", got, want) } } func TestNodeDestroyDeposedResourceInstanceObject_Execute(t *testing.T) { deposedKey := states.NewDeposedKey() state := states.NewState() absResource := mustResourceInstanceAddr("test_instance.foo") state.Module(addrs.RootModuleInstance).SetResourceInstanceDeposed( absResource.Resource, deposedKey, &states.ResourceInstanceObjectSrc{ Status: states.ObjectTainted, AttrsJSON: []byte(`{"id":"bar"}`), }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), ) schema := &ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_instance": { Attributes: map[string]*configschema.Attribute{ "id": { Type: cty.String, Computed: true, }, }, }, }, } p := testProvider("test") p.ConfigureProvider(providers.ConfigureProviderRequest{}) p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ UpgradedState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("bar"), }), } ctx := &MockEvalContext{ StateState: state.SyncWrapper(), ProviderProvider: p, ProviderSchemaSchema: schema, ChangesChanges: plans.NewChanges().SyncWrapper(), } node := NodeDestroyDeposedResourceInstanceObject{ NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ Addr: absResource, NodeAbstractResource: NodeAbstractResource{ ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), }, }, DeposedKey: deposedKey, } err := node.Execute(ctx, walkApply) if err != nil { t.Fatalf("unexpected error: %s", err) } if !state.Empty() { t.Fatalf("resources left in state after destroy") } } func TestNodeDestroyDeposedResourceInstanceObject_WriteResourceInstanceState(t *testing.T) { state := states.NewState() ctx := new(MockEvalContext) ctx.StateState = state.SyncWrapper() ctx.PathPath = addrs.RootModuleInstance mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": { Type: cty.String, Optional: true, }, }, }) ctx.ProviderProvider = mockProvider ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() obj := &states.ResourceInstanceObject{ Value: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-abc123"), }), Status: states.ObjectReady, } node := &NodeDestroyDeposedResourceInstanceObject{ NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ NodeAbstractResource: NodeAbstractResource{ ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), }, Addr: mustResourceInstanceAddr("aws_instance.foo"), }, DeposedKey: states.NewDeposedKey(), } err := node.writeResourceInstanceState(ctx, obj) if err != nil { t.Fatalf("unexpected error: %s", err.Error()) } checkStateString(t, state, ` aws_instance.foo: (1 deposed) ID = provider = provider["registry.terraform.io/hashicorp/aws"] Deposed ID 1 = i-abc123 `) } func TestNodeDestroyDeposedResourceInstanceObject_ExecuteMissingState(t *testing.T) { p := simpleMockProvider() ctx := &MockEvalContext{ StateState: states.NewState().SyncWrapper(), ProviderProvider: simpleMockProvider(), ProviderSchemaSchema: p.ProviderSchema(), ChangesChanges: plans.NewChanges().SyncWrapper(), } node := NodeDestroyDeposedResourceInstanceObject{ NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ Addr: mustResourceInstanceAddr("test_object.foo"), NodeAbstractResource: NodeAbstractResource{ ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), }, }, DeposedKey: states.NewDeposedKey(), } err := node.Execute(ctx, walkApply) if err == nil { t.Fatal("expected error") } }