diff --git a/terraform/context_refresh_test.go b/terraform/context_refresh_test.go index b7f3b6927..c38084ab4 100644 --- a/terraform/context_refresh_test.go +++ b/terraform/context_refresh_test.go @@ -1689,3 +1689,56 @@ aws_instance.foo: checkStateString(t, result, expect) } + +// verify that create_before_destroy is updated in the state during refresh +func TestRefresh_updateLifecycle(t *testing.T) { + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "aws_instance", + Name: "bar", + }.Instance(addrs.NoKey), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"bar"}`), + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("aws"), + Module: addrs.RootModule, + }, + ) + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "aws_instance" "bar" { + lifecycle { + create_before_destroy = true + } +} +`, + }) + + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + }, + State: state, + }) + + state, diags := ctx.Refresh() + if diags.HasErrors() { + t.Fatalf("plan errors: %s", diags.Err()) + } + + r := state.ResourceInstance(mustResourceInstanceAddr("aws_instance.bar")) + if !r.Current.CreateBeforeDestroy { + t.Fatal("create_before_destroy not updated in instance state") + } +} diff --git a/terraform/eval_state.go b/terraform/eval_state.go index 02f36d42c..6fcb9a858 100644 --- a/terraform/eval_state.go +++ b/terraform/eval_state.go @@ -529,3 +529,36 @@ func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) { return nil, nil } + +// EvalRefreshLifecycle is an EvalNode implementation that updates +// the status of the lifecycle options stored in the state. +// This currently only applies to create_before_destroy. +type EvalRefreshLifecycle struct { + Addr addrs.AbsResourceInstance + + Config *configs.Resource + // Prior State + State **states.ResourceInstanceObject + // ForceCreateBeforeDestroy indicates a create_before_destroy resource + // depends on this resource. + ForceCreateBeforeDestroy bool +} + +func (n *EvalRefreshLifecycle) Eval(ctx EvalContext) (interface{}, error) { + state := *n.State + if state == nil { + // no existing state + return nil, nil + } + + // In 0.13 we could be refreshing a resource with no config. + // We should be operating on managed resource, but check here to be certain + if n.Config == nil || n.Config.Managed == nil { + log.Printf("[WARN] EvalRefreshLifecycle: no Managed config value found in instance state for %q", n.Addr) + return nil, nil + } + + state.CreateBeforeDestroy = n.Config.Managed.CreateBeforeDestroy || n.ForceCreateBeforeDestroy + + return nil, nil +} diff --git a/terraform/node_resource_plan_instance.go b/terraform/node_resource_plan_instance.go index 523f0001d..2605b6f14 100644 --- a/terraform/node_resource_plan_instance.go +++ b/terraform/node_resource_plan_instance.go @@ -148,6 +148,12 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe ProviderSchema: &providerSchema, Output: &instanceRefreshState, }, + &EvalRefreshLifecycle{ + Addr: addr, + Config: n.Config, + State: &instanceRefreshState, + ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy, + }, &EvalRefresh{ Addr: addr.Resource, ProviderAddr: n.ResolvedProvider,