ensure data sources are always written to state
The old logic for `depends_on` was to short-circuit evaluation of the data source, but that prevented a plan and state from being recorded. Use the (currently unused) ForcePlanRead to ensure that the plan is recorded when the config contains `depends_on`. This does not fix the fact that depends on does not work with data sources, and will still produce a perpetual diff. This is only to fix evaluation errors when an indexed data source is evaluated during refresh.
This commit is contained in:
parent
e1d0acda0b
commit
aacfaa4fd7
|
@ -1906,3 +1906,61 @@ data "aws_data_source" "foo" {
|
||||||
t.Fatal("ValidateDataSourceConfig not called during plan")
|
t.Fatal("ValidateDataSourceConfig not called during plan")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext2Refresh_dataResourceDependsOn(t *testing.T) {
|
||||||
|
m := testModule(t, "plan-data-depends-on")
|
||||||
|
p := testProvider("test")
|
||||||
|
p.GetSchemaReturn = &ProviderSchema{
|
||||||
|
ResourceTypes: map[string]*configschema.Block{
|
||||||
|
"test_resource": {
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"id": {Type: cty.String, Computed: true},
|
||||||
|
"foo": {Type: cty.String, Optional: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DataSources: map[string]*configschema.Block{
|
||||||
|
"test_data": {
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"compute": {Type: cty.String, Computed: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
|
s := MustShimLegacyState(&State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: rootModulePath,
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"test_resource.a": &ResourceState{
|
||||||
|
Type: "test_resource",
|
||||||
|
Provider: "provider.test",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "a",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"id": "a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Config: m,
|
||||||
|
ProviderResolver: providers.ResolverFixed(
|
||||||
|
map[string]providers.Factory{
|
||||||
|
"test": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
State: s,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, diags := ctx.Refresh()
|
||||||
|
if diags.HasErrors() {
|
||||||
|
t.Fatalf("unexpected errors: %s", diags.Err())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -163,22 +163,6 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
|
||||||
ProviderSchema: &providerSchema,
|
ProviderSchema: &providerSchema,
|
||||||
},
|
},
|
||||||
|
|
||||||
&EvalIf{
|
|
||||||
If: func(ctx EvalContext) (bool, error) {
|
|
||||||
// If the config explicitly has a depends_on for this
|
|
||||||
// data source, assume the intention is to prevent
|
|
||||||
// refreshing ahead of that dependency, and therefore
|
|
||||||
// we need to deal with this resource during the apply
|
|
||||||
// phase..
|
|
||||||
if len(n.Config.DependsOn) > 0 {
|
|
||||||
return true, EvalEarlyExitError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
},
|
|
||||||
Then: EvalNoop{},
|
|
||||||
},
|
|
||||||
|
|
||||||
// EvalReadData will _attempt_ to read the data source, but may
|
// EvalReadData will _attempt_ to read the data source, but may
|
||||||
// generate an incomplete planned object if the configuration
|
// generate an incomplete planned object if the configuration
|
||||||
// includes values that won't be known until apply.
|
// includes values that won't be known until apply.
|
||||||
|
@ -192,6 +176,12 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
|
||||||
OutputChange: &change,
|
OutputChange: &change,
|
||||||
OutputConfigValue: &configVal,
|
OutputConfigValue: &configVal,
|
||||||
OutputState: &state,
|
OutputState: &state,
|
||||||
|
// If the config explicitly has a depends_on for this data
|
||||||
|
// source, assume the intention is to prevent refreshing ahead
|
||||||
|
// of that dependency, and therefore we need to deal with this
|
||||||
|
// resource during the apply phase. We do that by forcing this
|
||||||
|
// read to result in a plan.
|
||||||
|
ForcePlanRead: len(n.Config.DependsOn) > 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
&EvalIf{
|
&EvalIf{
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
resource "test_resource" "a" {
|
||||||
|
}
|
||||||
|
|
||||||
|
data "test_data" "d" {
|
||||||
|
count = 1
|
||||||
|
depends_on = [
|
||||||
|
test_resource.a
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_resource" "b" {
|
||||||
|
count = 1
|
||||||
|
foo = data.test_data.d[count.index].compute
|
||||||
|
}
|
Loading…
Reference in New Issue