mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-15 19:22:46 -06:00
1afdb055ca
This is no longer a call into the provider, since all of the data diff logic is standard for all data sources anyway. Instead, we just compute the planned new value and construct a planned change from that as-is. Previously the provider could, in principle, customize the read diff. In practice there is no real reason to do that and the existing SDK didn't pass that possibility through to provider code, so we can safely change this without impacting provider compatibility.
193 lines
5.3 KiB
Go
193 lines
5.3 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/configs"
|
|
"github.com/hashicorp/terraform/plans"
|
|
"github.com/hashicorp/terraform/plans/objchange"
|
|
"github.com/hashicorp/terraform/providers"
|
|
"github.com/hashicorp/terraform/states"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
// EvalReadDataDiff is an EvalNode implementation that executes a data
|
|
// resource's ReadDataDiff method to discover what attributes it exports.
|
|
type EvalReadDataDiff struct {
|
|
Addr addrs.ResourceInstance
|
|
Config *configs.Resource
|
|
ProviderAddr addrs.AbsProviderConfig
|
|
ProviderSchema **ProviderSchema
|
|
|
|
Output **plans.ResourceInstanceChange
|
|
OutputValue *cty.Value
|
|
OutputState **states.ResourceInstanceObject
|
|
|
|
// Set Previous when re-evaluating diff during apply, to ensure that
|
|
// the "Destroy" flag is preserved.
|
|
Previous **plans.ResourceInstanceChange
|
|
}
|
|
|
|
func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
|
absAddr := n.Addr.Absolute(ctx.Path())
|
|
|
|
if n.ProviderSchema == nil || *n.ProviderSchema == nil {
|
|
return nil, fmt.Errorf("provider schema not available for %s", n.Addr)
|
|
}
|
|
|
|
var diags tfdiags.Diagnostics
|
|
var change *plans.ResourceInstanceChange
|
|
var configVal cty.Value
|
|
|
|
if n.Previous != nil && *n.Previous != nil && (*n.Previous).Action == plans.Delete {
|
|
// If we're re-diffing for a diff that was already planning to
|
|
// destroy, then we'll just continue with that plan.
|
|
|
|
nullVal := cty.NullVal(cty.DynamicPseudoType)
|
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PreDiff(absAddr, states.CurrentGen, nullVal, nullVal)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
change = &plans.ResourceInstanceChange{
|
|
Addr: absAddr,
|
|
ProviderAddr: n.ProviderAddr,
|
|
Change: plans.Change{
|
|
Action: plans.Delete,
|
|
Before: nullVal,
|
|
After: nullVal,
|
|
},
|
|
}
|
|
} else {
|
|
config := *n.Config
|
|
providerSchema := *n.ProviderSchema
|
|
schema := providerSchema.DataSources[n.Addr.Resource.Type]
|
|
if schema == nil {
|
|
// Should be caught during validation, so we don't bother with a pretty error here
|
|
return nil, fmt.Errorf("provider does not support data source %q", n.Addr.Resource.Type)
|
|
}
|
|
|
|
objTy := schema.ImpliedType()
|
|
priorVal := cty.NullVal(objTy) // for data resources, prior is always null because we start fresh every time
|
|
|
|
keyData := EvalDataForInstanceKey(n.Addr.Key)
|
|
|
|
var configDiags tfdiags.Diagnostics
|
|
configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData)
|
|
diags = diags.Append(configDiags)
|
|
if configDiags.HasErrors() {
|
|
return nil, diags.Err()
|
|
}
|
|
|
|
proposedNewVal := objchange.ProposedNewObject(schema, priorVal, configVal)
|
|
|
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
change = &plans.ResourceInstanceChange{
|
|
Addr: absAddr,
|
|
ProviderAddr: n.ProviderAddr,
|
|
Change: plans.Change{
|
|
Action: plans.Read,
|
|
Before: priorVal,
|
|
After: proposedNewVal,
|
|
},
|
|
}
|
|
}
|
|
|
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PostDiff(absAddr, states.CurrentGen, change.Action, change.Before, change.After)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if n.Output != nil {
|
|
*n.Output = change
|
|
}
|
|
|
|
if n.OutputValue != nil {
|
|
*n.OutputValue = change.After
|
|
}
|
|
|
|
if n.OutputState != nil {
|
|
state := &states.ResourceInstanceObject{
|
|
Value: change.After,
|
|
Status: states.ObjectReady,
|
|
}
|
|
*n.OutputState = state
|
|
}
|
|
|
|
return nil, diags.ErrWithWarnings()
|
|
}
|
|
|
|
// EvalReadDataApply is an EvalNode implementation that executes a data
|
|
// resource's ReadDataApply method to read data from the data source.
|
|
type EvalReadDataApply struct {
|
|
Addr addrs.ResourceInstance
|
|
Provider *providers.Interface
|
|
Output **states.ResourceInstanceObject
|
|
Change **plans.ResourceInstanceChange
|
|
}
|
|
|
|
func (n *EvalReadDataApply) Eval(ctx EvalContext) (interface{}, error) {
|
|
return nil, fmt.Errorf("EvalReadDataApply not yet updated for new state/plan/provider types")
|
|
/*
|
|
provider := *n.Provider
|
|
change := *n.Change
|
|
absAddr := n.Addr.Absolute(ctx.Path())
|
|
|
|
// The provider and hook APIs still expect our legacy InstanceInfo type.
|
|
legacyInfo := NewInstanceInfo(n.Addr.Absolute(ctx.Path()))
|
|
|
|
// If the diff is for *destroying* this resource then we'll
|
|
// just drop its state and move on, since data resources don't
|
|
// support an actual "destroy" action.
|
|
if diff != nil && diff.GetDestroy() {
|
|
if n.Output != nil {
|
|
*n.Output = nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// For the purpose of external hooks we present a data apply as a
|
|
// "Refresh" rather than an "Apply" because creating a data source
|
|
// is presented to users/callers as a "read" operation.
|
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
|
// We don't have a state yet, so we'll just give the hook an
|
|
// empty one to work with.
|
|
return h.PreRefresh(absAddr, cty.NullVal(cty.DynamicPseudoType))
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
state, err := provider.ReadDataApply(legacyInfo, diff)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: %s", n.Addr.Absolute(ctx.Path()).String(), err)
|
|
}
|
|
|
|
err = ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PostRefresh(absAddr, state)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if n.Output != nil {
|
|
*n.Output = state
|
|
}
|
|
|
|
return nil, nil
|
|
*/
|
|
}
|