core: produce diff when data resource config becomes computed
Previously the plan phase would produce a data diff only if no state was already present. However, this is a faulty approach because a state will already be present in the case where the data resource depends on a managed resource that existed in state during refresh but became computed during plan, due to a "forces new resource" diff. Now we will produce a data diff regardless of the presence of the state when the configuration is computed during the plan phase. This fixes #6824.
This commit is contained in:
parent
10cc8b8c63
commit
f41fe4879e
|
@ -911,6 +911,93 @@ func TestContext2Plan_computedDataResource(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) {
|
||||
m := testModule(t, "plan-data-resource-becomes-computed")
|
||||
p := testProvider("aws")
|
||||
|
||||
p.DiffFn = func(info *InstanceInfo, state *InstanceState, config *ResourceConfig) (*InstanceDiff, error) {
|
||||
if info.Type != "aws_instance" {
|
||||
t.Fatalf("don't know how to diff %s", info.Id)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &InstanceDiff{
|
||||
Attributes: map[string]*ResourceAttrDiff{
|
||||
"computed": &ResourceAttrDiff{
|
||||
Old: "",
|
||||
New: "",
|
||||
NewComputed: true,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
p.ReadDataDiffReturn = &InstanceDiff{
|
||||
Attributes: map[string]*ResourceAttrDiff{
|
||||
"foo": &ResourceAttrDiff{
|
||||
Old: "",
|
||||
New: "",
|
||||
NewComputed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
State: &State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"data.aws_data_resource.foo": &ResourceState{
|
||||
Type: "aws_data_resource",
|
||||
Primary: &InstanceState{
|
||||
ID: "i-abc123",
|
||||
Attributes: map[string]string{
|
||||
"id": "i-abc123",
|
||||
"value": "baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
plan, err := ctx.Plan()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if got := len(plan.Diff.Modules); got != 1 {
|
||||
t.Fatalf("got %d modules; want 1", got)
|
||||
}
|
||||
|
||||
if !p.ReadDataDiffCalled {
|
||||
t.Fatal("ReadDataDiff wasn't called, but should've been")
|
||||
}
|
||||
if got, want := p.ReadDataDiffInfo.Id, "data.aws_data_resource.foo"; got != want {
|
||||
t.Fatalf("ReadDataDiff info id is %s; want %s", got, want)
|
||||
}
|
||||
|
||||
moduleDiff := plan.Diff.Modules[0]
|
||||
|
||||
iDiff, ok := moduleDiff.Resources["data.aws_data_resource.foo"]
|
||||
if !ok {
|
||||
t.Fatalf("missing diff for data.aws_data_resource.foo")
|
||||
}
|
||||
|
||||
if same, _ := p.ReadDataDiffReturn.Same(iDiff); !same {
|
||||
t.Fatalf(
|
||||
"incorrect diff for data.data_resource.foo\ngot: %#v\nwant: %#v",
|
||||
iDiff, p.ReadDataDiffReturn,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_computedList(t *testing.T) {
|
||||
m := testModule(t, "plan-computed-list")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
resource "aws_instance" "foo" {
|
||||
}
|
||||
|
||||
data "aws_data_resource" "foo" {
|
||||
foo = "${aws_instance.foo.computed}"
|
||||
}
|
|
@ -562,10 +562,6 @@ func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, in
|
|||
nodes := make([]EvalNode, 0, 5)
|
||||
|
||||
// Refresh the resource
|
||||
// TODO: Interpolate and then check if the config has any computed stuff.
|
||||
// If it doesn't, then do the diff/apply/writestate steps here so we
|
||||
// can get this data resource populated early enough for its values to
|
||||
// be visible during plan.
|
||||
nodes = append(nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkRefresh},
|
||||
Node: &EvalSequence{
|
||||
|
@ -654,12 +650,25 @@ func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, in
|
|||
Output: &state,
|
||||
},
|
||||
|
||||
// If we already have a state (created either during refresh
|
||||
// or on a previous apply) then we don't need to do any
|
||||
// more work on it during apply.
|
||||
// We need to re-interpolate the config here because some
|
||||
// of the attributes may have become computed during
|
||||
// earlier planning, due to other resources having
|
||||
// "requires new resource" diffs.
|
||||
&EvalInterpolate{
|
||||
Config: n.Resource.RawConfig.Copy(),
|
||||
Resource: resource,
|
||||
Output: &config,
|
||||
},
|
||||
|
||||
&EvalIf{
|
||||
If: func(ctx EvalContext) (bool, error) {
|
||||
if state != nil {
|
||||
computed := config.ComputedKeys != nil && len(config.ComputedKeys) > 0
|
||||
|
||||
// If the configuration is complete and we
|
||||
// already have a state then we don't need to
|
||||
// do any further work during apply, because we
|
||||
// already populated the state during refresh.
|
||||
if !computed && state != nil {
|
||||
return true, EvalEarlyExitError{}
|
||||
}
|
||||
|
||||
|
@ -668,12 +677,6 @@ func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, in
|
|||
Then: EvalNoop{},
|
||||
},
|
||||
|
||||
&EvalInterpolate{
|
||||
Config: n.Resource.RawConfig.Copy(),
|
||||
Resource: resource,
|
||||
Output: &config,
|
||||
},
|
||||
|
||||
&EvalGetProvider{
|
||||
Name: n.ProvidedBy()[0],
|
||||
Output: &provider,
|
||||
|
|
Loading…
Reference in New Issue