Prevent data sources from being aplied early

If a data source has explicit dependencies in `depends_on`, we can
assume the user has added those because of a dependency not tracked
directly in the config. If there are any entries in `depends_on`, don't
apply the data source early during Refresh.
This commit is contained in:
James Bardin 2016-12-12 10:29:36 -05:00
parent 1f5b940f3f
commit d2c6f1b57f
3 changed files with 78 additions and 0 deletions

View File

@ -6902,3 +6902,65 @@ module.middle.bottom:
t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
}
}
// If a data source explicitly depends on another resource, it's because we need
// that resource to be applied first.
func TestContext2Apply_dataDependsOn(t *testing.T) {
p := testProvider("null")
m := testModule(t, "apply-data-depends-on")
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"null": testProviderFuncFixed(p),
},
})
// the "provisioner" here writes to this variable, because the intent is to
// create a dependency which can't be viewed through the graph, and depends
// solely on the configuration providing "depends_on"
provisionerOutput := ""
p.ApplyFn = func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) {
// the side effect of the resource being applied
provisionerOutput = "APPLIED"
return testApplyFn(info, s, d)
}
p.DiffFn = testDiffFn
p.ReadDataDiffFn = testDataDiffFn
p.ReadDataApplyFn = func(*InstanceInfo, *InstanceDiff) (*InstanceState, error) {
// Read the artifact created by our dependency being applied.
// Without any "depends_on", this would be skipped as it's assumed the
// initial diff during refresh was all that's needed.
return &InstanceState{
ID: "read",
Attributes: map[string]string{
"foo": provisionerOutput,
},
}, nil
}
_, err := ctx.Refresh()
if err != nil {
t.Fatalf("err: %s", err)
}
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
root := state.ModuleByPath(RootModulePath)
actual := root.Resources["data.null_data_source.read"].Primary.Attributes["foo"]
expected := "APPLIED"
if actual != expected {
t.Fatalf("bad:\n%s", strings.TrimSpace(state.String()))
}
}

View File

@ -0,0 +1,8 @@
resource "null_resource" "write" {
foo = "attribute"
}
data "null_data_source" "read" {
foo = ""
depends_on = ["null_resource.write"]
}

View File

@ -601,10 +601,18 @@ func (n *graphNodeExpandedResource) dataResourceEvalNodes(resource *Resource, in
// apply phases.)
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 {
return true, EvalEarlyExitError{}
}
// If the config explicitly has a depends_on for this
// data source, assume the intention is to prevent
// refreshing ahead of that dependency.
if len(n.Resource.DependsOn) > 0 {
return true, EvalEarlyExitError{}
}
return true, nil
},
Then: EvalNoop{},