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) {
|
func TestContext2Plan_computedList(t *testing.T) {
|
||||||
m := testModule(t, "plan-computed-list")
|
m := testModule(t, "plan-computed-list")
|
||||||
p := testProvider("aws")
|
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)
|
nodes := make([]EvalNode, 0, 5)
|
||||||
|
|
||||||
// Refresh the resource
|
// 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{
|
nodes = append(nodes, &EvalOpFilter{
|
||||||
Ops: []walkOperation{walkRefresh},
|
Ops: []walkOperation{walkRefresh},
|
||||||
Node: &EvalSequence{
|
Node: &EvalSequence{
|
||||||
|
@ -654,12 +650,25 @@ func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, in
|
||||||
Output: &state,
|
Output: &state,
|
||||||
},
|
},
|
||||||
|
|
||||||
// If we already have a state (created either during refresh
|
// We need to re-interpolate the config here because some
|
||||||
// or on a previous apply) then we don't need to do any
|
// of the attributes may have become computed during
|
||||||
// more work on it during apply.
|
// earlier planning, due to other resources having
|
||||||
|
// "requires new resource" diffs.
|
||||||
|
&EvalInterpolate{
|
||||||
|
Config: n.Resource.RawConfig.Copy(),
|
||||||
|
Resource: resource,
|
||||||
|
Output: &config,
|
||||||
|
},
|
||||||
|
|
||||||
&EvalIf{
|
&EvalIf{
|
||||||
If: func(ctx EvalContext) (bool, error) {
|
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{}
|
return true, EvalEarlyExitError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,12 +677,6 @@ func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, in
|
||||||
Then: EvalNoop{},
|
Then: EvalNoop{},
|
||||||
},
|
},
|
||||||
|
|
||||||
&EvalInterpolate{
|
|
||||||
Config: n.Resource.RawConfig.Copy(),
|
|
||||||
Resource: resource,
|
|
||||||
Output: &config,
|
|
||||||
},
|
|
||||||
|
|
||||||
&EvalGetProvider{
|
&EvalGetProvider{
|
||||||
Name: n.ProvidedBy()[0],
|
Name: n.ProvidedBy()[0],
|
||||||
Output: &provider,
|
Output: &provider,
|
||||||
|
|
Loading…
Reference in New Issue