Skip to content

Commit 22d8d94

Browse files
committed
check for data source changed during plan
Rather than re-read the data source during every plan cycle, apply the config to the prior state, and skip reading if there is no change. Remove the TODOs, as we're going to accept that data-only changes will still not be plan-able for the time being.
1 parent 13101ae commit 22d8d94

File tree

4 files changed

+35
-20
lines changed

4 files changed

+35
-20
lines changed

terraform/context_apply_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8782,6 +8782,16 @@ func TestContext2Apply_dataDependsOn(t *testing.T) {
87828782
if actual != expected {
87838783
t.Fatalf("bad:\n%s", strings.TrimSpace(state.String()))
87848784
}
8785+
8786+
// run another plan to make sure the data source doesn't show as a change
8787+
plan, diags := ctx.Plan()
8788+
assertNoErrors(t, diags)
8789+
8790+
for _, c := range plan.Changes.Resources {
8791+
if c.Action != plans.NoOp {
8792+
t.Fatalf("unexpected change for %s", c.Addr)
8793+
}
8794+
}
87858795
}
87868796

87878797
func TestContext2Apply_terraformWorkspace(t *testing.T) {

terraform/context_plan_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,8 +1892,9 @@ func TestContext2Plan_computedInFunction(t *testing.T) {
18921892
_, diags = ctx.Plan() // should do nothing with data resource in this step, since it was already read
18931893
assertNoErrors(t, diags)
18941894

1895-
if !p.ReadDataSourceCalled {
1896-
t.Fatalf("ReadDataSource should have been called")
1895+
if p.ReadDataSourceCalled {
1896+
// there was no config change to read during plan
1897+
t.Fatalf("ReadDataSource should not have been called")
18971898
}
18981899
}
18991900

terraform/eval_read_data.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -236,19 +236,15 @@ func (n *EvalReadDataRefresh) Eval(ctx EvalContext) (interface{}, error) {
236236
}
237237

238238
configKnown := configVal.IsWhollyKnown()
239-
// If our configuration contains any unknown values, or we depend on any
240-
// unknown values then we must defer the read to the apply phase by
241-
// producing a "Read" change for this resource, and a placeholder value for
242-
// it in the state.
243-
if len(n.Config.DependsOn) > 0 || !configKnown {
239+
// If our configuration contains any unknown values, then we must defer the
240+
// read until plan or apply.
241+
if !configKnown {
244242
if configKnown {
245243
log.Printf("[TRACE] EvalReadDataRefresh: %s configuration is fully known, but we're forcing a read plan to be created", absAddr)
246244
} else {
247245
log.Printf("[TRACE] EvalReadDataRefresh: %s configuration not fully known yet, so deferring to apply phase", absAddr)
248246
}
249247

250-
proposedNewVal := objchange.PlannedDataResourceObject(schema, configVal)
251-
252248
// We need to store a change so tat other references to this data
253249
// source can resolve correctly, since the state is not going to be up
254250
// to date.
@@ -258,7 +254,7 @@ func (n *EvalReadDataRefresh) Eval(ctx EvalContext) (interface{}, error) {
258254
Change: plans.Change{
259255
Action: plans.Read,
260256
Before: priorVal,
261-
After: proposedNewVal,
257+
After: objchange.PlannedDataResourceObject(schema, configVal),
262258
},
263259
}
264260

@@ -267,12 +263,7 @@ func (n *EvalReadDataRefresh) Eval(ctx EvalContext) (interface{}, error) {
267263
}
268264
if n.State != nil {
269265
*n.State = &states.ResourceInstanceObject{
270-
// We need to keep the prior value in the state so that plan
271-
// has something to diff against.
272-
Value: priorVal,
273-
// TODO: this needs to be ObjectPlanned to trigger a plan, but
274-
// the prior value is lost preventing plan from resulting in a
275-
// NoOp
266+
Value: cty.NullVal(objTy),
276267
Status: states.ObjectPlanned,
277268
}
278269
}
@@ -286,9 +277,8 @@ func (n *EvalReadDataRefresh) Eval(ctx EvalContext) (interface{}, error) {
286277
return nil, diags.ErrWithWarnings()
287278
}
288279

289-
// TODO: Need to signal to plan that this may have changed. We may be able
290-
// to use ObjectPlanned for that, but that currently causes the state to be
291-
// dropped altogether
280+
// This may still have been refreshed with references to resources that
281+
// will be updated, but that will be caught as a change during plan.
292282
outputState := &states.ResourceInstanceObject{
293283
Value: newVal,
294284
Status: states.ObjectReady,

terraform/eval_read_data_plan.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func (n *EvalReadDataPlan) Eval(ctx EvalContext) (interface{}, error) {
6161
}
6262

6363
configKnown := configVal.IsWhollyKnown()
64-
proposedNewVal := objchange.PlannedDataResourceObject(schema, configVal)
6564
// If our configuration contains any unknown values, or we depend on any
6665
// unknown values then we must defer the read to the apply phase by
6766
// producing a "Read" change for this resource, and a placeholder value for
@@ -73,6 +72,8 @@ func (n *EvalReadDataPlan) Eval(ctx EvalContext) (interface{}, error) {
7372
log.Printf("[TRACE] EvalReadDataPlan: %s configuration not fully known yet, so deferring to apply phase", absAddr)
7473
}
7574

75+
proposedNewVal := objchange.PlannedDataResourceObject(schema, configVal)
76+
7677
err := ctx.Hook(func(h Hook) (HookAction, error) {
7778
return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal)
7879
})
@@ -101,6 +102,19 @@ func (n *EvalReadDataPlan) Eval(ctx EvalContext) (interface{}, error) {
101102
return nil, diags.ErrWithWarnings()
102103
}
103104

105+
// If we have a stored state we may not need to re-read the data source.
106+
// Check the config against the state to see if there are any difference.
107+
if !priorVal.IsNull() {
108+
// Applying the configuration to the prior state lets us see if there
109+
// are any differences.
110+
proposed := objchange.ProposedNewObject(schema, priorVal, configVal)
111+
if proposed.Equals(priorVal).True() {
112+
log.Printf("[TRACE] EvalReadDataPlan: %s no change detected, using existing state", absAddr)
113+
// state looks up to date, and must have been read during refresh
114+
return nil, diags.ErrWithWarnings()
115+
}
116+
}
117+
104118
newVal, readDiags := n.readDataSource(ctx, configVal)
105119
diags = diags.Append(readDiags)
106120
if diags.HasErrors() {

0 commit comments

Comments
 (0)