mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
ab29eca045
In an attempt to always show "id" as computed we were producing a synthetic diff for it, but this causes problems when the id attribute for a particular data source is actually settable in configuration, since it masks the setting from the config and forces it to always be empty. Instead, we'll set it conditionally so that a value provided in the config can shine through when available.
140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// EvalReadDataDiff is an EvalNode implementation that executes a data
|
|
// resource's ReadDataDiff method to discover what attributes it exports.
|
|
type EvalReadDataDiff struct {
|
|
Provider *ResourceProvider
|
|
Output **InstanceDiff
|
|
OutputState **InstanceState
|
|
Config **ResourceConfig
|
|
Info *InstanceInfo
|
|
|
|
// Set Previous when re-evaluating diff during apply, to ensure that
|
|
// the "Destroy" flag is preserved.
|
|
Previous **InstanceDiff
|
|
}
|
|
|
|
func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
|
// TODO: test
|
|
|
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PreDiff(n.Info, nil)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var diff *InstanceDiff
|
|
|
|
if n.Previous != nil && *n.Previous != nil && (*n.Previous).GetDestroy() {
|
|
// If we're re-diffing for a diff that was already planning to
|
|
// destroy, then we'll just continue with that plan.
|
|
diff = &InstanceDiff{Destroy: true}
|
|
} else {
|
|
provider := *n.Provider
|
|
config := *n.Config
|
|
|
|
var err error
|
|
diff, err = provider.ReadDataDiff(n.Info, config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if diff == nil {
|
|
diff = new(InstanceDiff)
|
|
}
|
|
|
|
// if id isn't explicitly set then it's always computed, because we're
|
|
// always "creating a new resource".
|
|
diff.init()
|
|
if _, ok := diff.Attributes["id"]; !ok {
|
|
diff.SetAttribute("id", &ResourceAttrDiff{
|
|
Old: "",
|
|
NewComputed: true,
|
|
RequiresNew: true,
|
|
Type: DiffAttrOutput,
|
|
})
|
|
}
|
|
}
|
|
|
|
err = ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PostDiff(n.Info, diff)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
*n.Output = diff
|
|
|
|
if n.OutputState != nil {
|
|
state := &InstanceState{}
|
|
*n.OutputState = state
|
|
|
|
// Apply the diff to the returned state, so the state includes
|
|
// any attribute values that are not computed.
|
|
if !diff.Empty() && n.OutputState != nil {
|
|
*n.OutputState = state.MergeDiff(diff)
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// EvalReadDataApply is an EvalNode implementation that executes a data
|
|
// resource's ReadDataApply method to read data from the data source.
|
|
type EvalReadDataApply struct {
|
|
Provider *ResourceProvider
|
|
Output **InstanceState
|
|
Diff **InstanceDiff
|
|
Info *InstanceInfo
|
|
}
|
|
|
|
func (n *EvalReadDataApply) Eval(ctx EvalContext) (interface{}, error) {
|
|
// TODO: test
|
|
provider := *n.Provider
|
|
diff := *n.Diff
|
|
|
|
// 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(n.Info, &InstanceState{})
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
state, err := provider.ReadDataApply(n.Info, diff)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: %s", n.Info.Id, err)
|
|
}
|
|
|
|
err = ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PostRefresh(n.Info, state)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if n.Output != nil {
|
|
*n.Output = state
|
|
}
|
|
|
|
return nil, nil
|
|
}
|