data source depends_on

A data source referencing another data source through depends_on should
not be forced to defer until apply. Data sources have no side effects,
so nothing should need to be applied. If the dependency has a
planned change due to a managed resource, the original data source will
also encounter that further down the list of dependencies.

This prevents a data source being read during plan for any reason from
causing other data sources to be deferred until apply. It does not
change the behavior noticeably in 0.14, but because 0.13 still had
separate refresh and plan phases which could read the data source, the
deferral could cause many things downstream to become unexpectedly
unknown until apply.
This commit is contained in:
James Bardin 2020-09-25 09:49:52 -04:00
parent ef64df950c
commit ea9096fb21
2 changed files with 67 additions and 0 deletions

View File

@ -6414,3 +6414,61 @@ resource "test_instance" "a" {
t.Fatal("Resource should not have been refreshed")
}
}
func TestContext2Plan_dataInModuleDependsOn(t *testing.T) {
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
readDataSourceB := false
p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
cfg := req.Config.AsValueMap()
foo := cfg["foo"].AsString()
cfg["id"] = cty.StringVal("ID")
cfg["foo"] = cty.StringVal("new")
if foo == "b" {
readDataSourceB = true
}
resp.State = cty.ObjectVal(cfg)
return resp
}
m := testModuleInline(t, map[string]string{
"main.tf": `
module "a" {
source = "./mod_a"
}
module "b" {
source = "./mod_b"
depends_on = [module.a]
}`,
"mod_a/main.tf": `
data "test_data_source" "a" {
foo = "a"
}`,
"mod_b/main.tf": `
data "test_data_source" "b" {
foo = "b"
}`,
})
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
// The change to data source a should not prevent data source b from being
// read.
if !readDataSourceB {
t.Fatal("data source b was not read during plan")
}
}

View File

@ -7,6 +7,7 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/plans/objchange"
"github.com/hashicorp/terraform/states"
@ -154,6 +155,14 @@ func (n *evalReadDataPlan) forcePlanRead(ctx EvalContext) bool {
// configuration.
changes := ctx.Changes()
for _, d := range n.dependsOn {
if d.Resource.Mode == addrs.DataResourceMode {
// Data sources have no external side effects, so they pose a need
// to delay this read. If they do have a change planned, it must be
// because of a dependency on a managed resource, in which case
// we'll also encounter it in this list of dependencies.
continue
}
for _, change := range changes.GetChangesForConfigResource(d) {
if change != nil && change.Action != plans.NoOp {
return true