append dependencies during refresh

Refresh should load any new dependencies found because of configuration
or state changes, but retain any dependencies already in the state.
Orphaned resources would not be in config, but we do not want to lose
the destroy ordering for the later apply.
This commit is contained in:
James Bardin 2019-10-28 21:16:45 -04:00
parent 886af20f07
commit 5e16e8eece
5 changed files with 69 additions and 4 deletions

View File

@ -1992,6 +1992,17 @@ func TestRefresh_updateDependencies(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"bar","foo":"foo"}`),
Dependencies: []addrs.AbsResource{
// Existing dependencies should not be removed during refresh
{
Module: addrs.RootModuleInstance,
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "baz",
},
},
},
},
addrs.ProviderConfig{
Type: "aws",
@ -2034,6 +2045,7 @@ aws_instance.bar:
foo = foo
Dependencies:
aws_instance.baz
aws_instance.foo
aws_instance.foo:
ID = foo

View File

@ -89,6 +89,7 @@ func (n *EvalRefresh) Eval(ctx EvalContext) (interface{}, error) {
newState := state.DeepCopy()
newState.Value = resp.NewState
newState.Private = resp.Private
newState.Dependencies = state.Dependencies
// Call post-refresh hook
err = ctx.Hook(func(h Hook) (HookAction, error) {

View File

@ -3,6 +3,7 @@ package terraform
import (
"fmt"
"log"
"sort"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
@ -203,7 +204,7 @@ type EvalWriteState struct {
// Dependencies are the inter-resource dependencies to be stored in the
// state.
Dependencies []addrs.AbsResource
Dependencies *[]addrs.AbsResource
}
func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
@ -228,7 +229,10 @@ func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
}
// store the new deps in the state
obj.Dependencies = n.Dependencies
if n.Dependencies != nil {
log.Printf("[TRACE] EvalWriteState: recording %d dependencies for %s", len(*n.Dependencies), absAddr)
obj.Dependencies = *n.Dependencies
}
if n.ProviderSchema == nil || *n.ProviderSchema == nil {
// Should never happen, unless our state object is nil
@ -480,3 +484,44 @@ func (n *EvalForgetResourceState) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil
}
// EvalRefreshDependencies is an EvalNode implementation that appends any newly
// found dependencies to those saved in the state. The existing dependencies
// are retained, as they may be missing from the config, and will be required
// for the updates and destroys during the next apply.
type EvalRefreshDependencies struct {
// Prior State
State **states.ResourceInstanceObject
// Dependencies to write to the new state
Dependencies *[]addrs.AbsResource
}
func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
state := *n.State
if state == nil {
// no existing state to append
return nil, nil
}
depMap := make(map[string]addrs.AbsResource)
for _, d := range *n.Dependencies {
depMap[d.String()] = d
}
for _, d := range state.Dependencies {
depMap[d.String()] = d
}
deps := make([]addrs.AbsResource, 0, len(depMap))
for _, d := range depMap {
deps = append(deps, d)
}
sort.Slice(deps, func(i, j int) bool {
return deps[i].String() < deps[j].String()
})
*n.Dependencies = deps
return nil, nil
}

View File

@ -367,7 +367,7 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema,
State: &state,
Dependencies: n.Dependencies,
Dependencies: &n.Dependencies,
},
&EvalApplyProvisioners{
Addr: addr.Resource,
@ -389,7 +389,7 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema,
State: &state,
Dependencies: n.Dependencies,
Dependencies: &n.Dependencies,
},
&EvalIf{
If: func(ctx EvalContext) (bool, error) {

View File

@ -214,6 +214,11 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
Output: &state,
},
&EvalRefreshDependencies{
State: &state,
Dependencies: &n.Dependencies,
},
&EvalRefresh{
Addr: addr.Resource,
ProviderAddr: n.ResolvedProvider,
@ -228,6 +233,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema,
State: &state,
Dependencies: &n.Dependencies,
},
},
}
@ -287,6 +293,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState(
ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema,
State: &state,
Dependencies: &n.Dependencies,
},
// We must also save the planned change, so that expressions in