we no longer need EvalRefreshDependencies

This evaluation was required when refresh ran in a separate walk and
managed resources were only partly handled by configuration. Now that we
have the correct dependency information available when refreshing
configured resources, we can update their state accordingly. Since
orphaned resources are not refreshed, they can retain their stored
dependencies for correct ordering.

This also prevents users from introducing cycles with nodes they can't
"see", since only orphaned nodes will retain their stored dependencies,
and the remaining nodes will be updated according to the configuration.
This commit is contained in:
James Bardin 2020-09-23 10:05:32 -04:00
parent bd5aeb3d45
commit def1f9b084
3 changed files with 35 additions and 56 deletions

View File

@ -1582,7 +1582,7 @@ func TestRefresh_updateDependencies(t *testing.T) {
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.ConfigResource{
// Existing dependencies should not be removed during refresh
// Existing dependencies should be removed when overridden by the config
{
Module: addrs.RootModule,
Resource: addrs.Resource{
@ -1598,6 +1598,32 @@ func TestRefresh_updateDependencies(t *testing.T) {
Module: addrs.RootModule,
},
)
root.SetResourceInstanceCurrent(
addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "baz",
}.Instance(addrs.NoKey),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"baz"}`),
Dependencies: []addrs.ConfigResource{
// Existing dependencies should not be removed from orphaned instances
{
Module: addrs.RootModule,
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "bam",
},
},
},
},
addrs.AbsProviderConfig{
Provider: addrs.NewDefaultProvider("aws"),
Module: addrs.RootModule,
},
)
root.SetResourceInstanceCurrent(
addrs.Resource{
Mode: addrs.ManagedResourceMode,
@ -1621,7 +1647,8 @@ resource "aws_instance" "bar" {
}
resource "aws_instance" "foo" {
}`,
}
`,
})
p := testProvider("aws")
@ -1649,12 +1676,15 @@ aws_instance.bar:
Dependencies:
aws_instance.foo
aws_instance.foo:
ID = foo
aws_instance.baz:
ID = baz
provider = provider["registry.terraform.io/hashicorp/aws"]
Dependencies:
aws_instance.baz
aws_instance.bam
aws_instance.foo:
ID = foo
provider = provider["registry.terraform.io/hashicorp/aws"]
`)
checkStateString(t, result, expect)

View File

@ -3,7 +3,6 @@ package terraform
import (
"fmt"
"log"
"sort"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
@ -530,49 +529,3 @@ func (n *EvalWriteResourceState) 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.ConfigResource
}
func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
state := *n.State
if state == nil {
// no existing state to append
return nil, nil
}
// We already have dependencies in state, so we need to trust those for
// refresh. We can't write out new dependencies until apply time in case
// the configuration has been changed in a manner the conflicts with the
// stored dependencies.
if len(state.Dependencies) > 0 {
*n.Dependencies = state.Dependencies
return nil, nil
}
depMap := make(map[string]addrs.ConfigResource)
for _, d := range *n.Dependencies {
depMap[d.String()] = d
}
deps := make([]addrs.ConfigResource, 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

@ -141,10 +141,6 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
ProviderSchema: &providerSchema,
Output: &instanceRefreshState,
},
&EvalRefreshDependencies{
State: &instanceRefreshState,
Dependencies: &n.Dependencies,
},
&EvalRefresh{
Addr: addr.Resource,
ProviderAddr: n.ResolvedProvider,