delay data source reads with pending resource ref
Treat any reference from a data source to a managed resource as a dependency on the entire resource. While a resource's attribute may be statically resolvable from the configuration, if the user added a reference to that resource, it stands to reason that the user intended there to be a dependency which we need to wait on. This is an extension of an implicit behavior that existed previously in Terraform, but was lost in the 0.13 release. That behavior was emergent from the fact that the Refresh walk did not process the configuration for managed resources, so any new resources in the config would be evaluate as entirely unknown during Refresh, even if some attributes were statically resolvable at that point. This new implementation restores the old behavior, and extends it to updates and replacements of the referenced resource.
This commit is contained in:
parent
6d7904c17b
commit
8b31808843
|
@ -6370,3 +6370,41 @@ data "test_data_source" "d" {
|
|||
t.Fatal("expected data.test_data_source.d to be fully read in refreshed state, got status", d.Current.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_dataReferencesResource(t *testing.T) {
|
||||
p := testProvider("test")
|
||||
p.ApplyFn = testApplyFn
|
||||
p.DiffFn = testDiffFn
|
||||
|
||||
p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source should not be read"))
|
||||
return resp
|
||||
}
|
||||
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
locals {
|
||||
x = "value"
|
||||
}
|
||||
|
||||
resource "test_resource" "a" {
|
||||
value = local.x
|
||||
}
|
||||
|
||||
// test_resource.a.value can be resolved during plan, but the reference implies
|
||||
// that the data source should wait until the resource is created.
|
||||
data "test_data_source" "d" {
|
||||
foo = test_resource.a.value
|
||||
}
|
||||
`})
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
_, diags := ctx.Plan()
|
||||
assertNoErrors(t, diags)
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ type GraphNodeAttachDependencies interface {
|
|||
// graphNodeDependsOn is implemented by resources that need to expose any
|
||||
// references set via DependsOn in their configuration.
|
||||
type graphNodeDependsOn interface {
|
||||
GraphNodeReferencer
|
||||
DependsOn() []*addrs.Reference
|
||||
}
|
||||
|
||||
|
@ -327,6 +328,31 @@ func (m ReferenceMap) dependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Ve
|
|||
|
||||
refs := depender.DependsOn()
|
||||
|
||||
// For data sources we implicitly treat references as depends_on entries.
|
||||
// If a data source references a resource, even if that reference is
|
||||
// resolvable, it stands to reason that the user intends for the data
|
||||
// source to require that resource in some way.
|
||||
if n, ok := depender.(GraphNodeConfigResource); ok &&
|
||||
n.ResourceAddr().Resource.Mode == addrs.DataResourceMode {
|
||||
for _, r := range depender.References() {
|
||||
|
||||
// We don't need to wait on referenced data sources. They have no
|
||||
// side effects, so our configuration reference should suffice for
|
||||
// proper ordering.
|
||||
var resAddr addrs.Resource
|
||||
switch s := r.Subject.(type) {
|
||||
case addrs.Resource:
|
||||
resAddr = s
|
||||
case addrs.ResourceInstance:
|
||||
resAddr = s.Resource
|
||||
}
|
||||
|
||||
if resAddr.Mode == addrs.ManagedResourceMode {
|
||||
refs = append(refs, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is where we record that a module has depends_on configured.
|
||||
if _, ok := depender.(*nodeExpandModule); ok && len(refs) > 0 {
|
||||
fromModule = true
|
||||
|
|
Loading…
Reference in New Issue