opentofu/terraform/eval_read_data_apply.go
James Bardin 6039622111 Simplify data lifecycle for the no-refresh world
Now that we don't have to handle data sources that may or may not have
been updated during a refresh phase, and the plan phase can save the
data source to the refreshed state, we can remove a lot of the logic
involved in detecting whether the data source needs to be planned or
not.

When there is no separate refresh phase, we always must attempt to read
the data source during planning, and the only conditions are based on
having a known configuration, and not having any dependencies on which
we're waiting. If the data source is read during plan, we can now save
that directly to the refreshed state, and don't need to smuggle the
value as a change to be saved during apply.
2020-09-22 09:55:19 -04:00

83 lines
2.4 KiB
Go

package terraform
import (
"fmt"
"github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
)
// evalReadDataApply is an EvalNode implementation that deals with the main part
// of the data resource lifecycle: either actually reading from the data source
// or generating a plan to do so.
type evalReadDataApply struct {
evalReadData
}
func (n *evalReadDataApply) Eval(ctx EvalContext) (interface{}, error) {
absAddr := n.Addr.Absolute(ctx.Path())
var diags tfdiags.Diagnostics
var planned *plans.ResourceInstanceChange
if n.Planned != nil {
planned = *n.Planned
}
if n.ProviderSchema == nil || *n.ProviderSchema == nil {
return nil, fmt.Errorf("provider schema not available for %s", n.Addr)
}
if planned != nil && planned.Action != plans.Read {
// If any other action gets in here then that's always a bug; this
// EvalNode only deals with reading.
return nil, fmt.Errorf(
"invalid action %s for %s: only Read is supported (this is a bug in Terraform; please report it!)",
planned.Action, absAddr,
)
}
if err := ctx.Hook(func(h Hook) (HookAction, error) {
return h.PreApply(absAddr, states.CurrentGen, planned.Action, planned.Before, planned.After)
}); err != nil {
return nil, err
}
config := *n.Config
providerSchema := *n.ProviderSchema
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource())
if schema == nil {
// Should be caught during validation, so we don't bother with a pretty error here
return nil, fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.Provider.String(), n.Addr.Resource.Type)
}
forEach, _ := evaluateForEachExpression(config.ForEach, ctx)
keyData := EvalDataForInstanceKey(n.Addr.Key, forEach)
configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags.ErrWithWarnings()
}
newVal, readDiags := n.readDataSource(ctx, configVal)
diags = diags.Append(readDiags)
if diags.HasErrors() {
return nil, diags.ErrWithWarnings()
}
*n.State = &states.ResourceInstanceObject{
Value: newVal,
Status: states.ObjectReady,
}
if err := ctx.Hook(func(h Hook) (HookAction, error) {
return h.PostApply(absAddr, states.CurrentGen, newVal, diags.Err())
}); err != nil {
diags = diags.Append(err)
}
return nil, diags.ErrWithWarnings()
}