core: Re-implement ReadDataDiff around our new approach
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.
This commit is contained in:
parent
3f8a973846
commit
1afdb055ca
|
@ -716,7 +716,7 @@ func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
changes := ctx.Changes()
|
changes := ctx.Changes()
|
||||||
addr := n.Addr.Absolute(ctx.Path())
|
addr := n.Addr.Absolute(ctx.Path())
|
||||||
|
|
||||||
schema := providerSchema.ResourceTypes[n.Addr.Resource.Type]
|
schema := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource())
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
// Should be caught during validation, so we don't bother with a pretty error here
|
// Should be caught during validation, so we don't bother with a pretty error here
|
||||||
return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type)
|
return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type)
|
||||||
|
@ -777,7 +777,7 @@ func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
panic("inconsistent address and/or deposed key in EvalWriteDiff")
|
panic("inconsistent address and/or deposed key in EvalWriteDiff")
|
||||||
}
|
}
|
||||||
|
|
||||||
schema := providerSchema.ResourceTypes[n.Addr.Resource.Type]
|
schema := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource())
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
// Should be caught during validation, so we don't bother with a pretty error here
|
// Should be caught during validation, so we don't bother with a pretty error here
|
||||||
return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type)
|
return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type)
|
||||||
|
|
|
@ -3,14 +3,15 @@ package terraform
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/providers"
|
|
||||||
"github.com/hashicorp/terraform/states"
|
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
"github.com/hashicorp/terraform/plans"
|
"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
|
// EvalReadDataDiff is an EvalNode implementation that executes a data
|
||||||
|
@ -18,7 +19,7 @@ import (
|
||||||
type EvalReadDataDiff struct {
|
type EvalReadDataDiff struct {
|
||||||
Addr addrs.ResourceInstance
|
Addr addrs.ResourceInstance
|
||||||
Config *configs.Resource
|
Config *configs.Resource
|
||||||
Provider *providers.Interface
|
ProviderAddr addrs.AbsProviderConfig
|
||||||
ProviderSchema **ProviderSchema
|
ProviderSchema **ProviderSchema
|
||||||
|
|
||||||
Output **plans.ResourceInstanceChange
|
Output **plans.ResourceInstanceChange
|
||||||
|
@ -31,8 +32,6 @@ type EvalReadDataDiff struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
return nil, fmt.Errorf("EvalReadDataDiff not yet updated for new state/plan/provider types")
|
|
||||||
/*
|
|
||||||
absAddr := n.Addr.Absolute(ctx.Path())
|
absAddr := n.Addr.Absolute(ctx.Path())
|
||||||
|
|
||||||
if n.ProviderSchema == nil || *n.ProviderSchema == nil {
|
if n.ProviderSchema == nil || *n.ProviderSchema == nil {
|
||||||
|
@ -40,26 +39,31 @@ func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
|
var change *plans.ResourceInstanceChange
|
||||||
|
var configVal cty.Value
|
||||||
|
|
||||||
// The provider API still expects our legacy InstanceInfo type.
|
if n.Previous != nil && *n.Previous != nil && (*n.Previous).Action == plans.Delete {
|
||||||
legacyInfo := NewInstanceInfo(n.Addr.Absolute(ctx.Path()))
|
// 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) {
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
||||||
return h.PreDiff(absAddr, cty.NullVal(cty.DynamicPseudoType), cty.NullVal(cty.DynamicPseudoType))
|
return h.PreDiff(absAddr, states.CurrentGen, nullVal, nullVal)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var diff *InstanceDiff
|
change = &plans.ResourceInstanceChange{
|
||||||
var configVal cty.Value
|
Addr: absAddr,
|
||||||
|
ProviderAddr: n.ProviderAddr,
|
||||||
if n.Previous != nil && *n.Previous != nil && (*n.Previous).GetDestroy() {
|
Change: plans.Change{
|
||||||
// If we're re-diffing for a diff that was already planning to
|
Action: plans.Delete,
|
||||||
// destroy, then we'll just continue with that plan.
|
Before: nullVal,
|
||||||
diff = &InstanceDiff{Destroy: true}
|
After: nullVal,
|
||||||
|
},
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
provider := *n.Provider
|
|
||||||
config := *n.Config
|
config := *n.Config
|
||||||
providerSchema := *n.ProviderSchema
|
providerSchema := *n.ProviderSchema
|
||||||
schema := providerSchema.DataSources[n.Addr.Resource.Type]
|
schema := providerSchema.DataSources[n.Addr.Resource.Type]
|
||||||
|
@ -68,6 +72,9 @@ func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
return nil, fmt.Errorf("provider does not support data source %q", n.Addr.Resource.Type)
|
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)
|
keyData := EvalDataForInstanceKey(n.Addr.Key)
|
||||||
|
|
||||||
var configDiags tfdiags.Diagnostics
|
var configDiags tfdiags.Diagnostics
|
||||||
|
@ -77,58 +84,50 @@ func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
return nil, diags.Err()
|
return nil, diags.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The provider API still expects our legacy ResourceConfig type.
|
proposedNewVal := objchange.ProposedNewObject(schema, priorVal, configVal)
|
||||||
legacyRC := NewResourceConfigShimmed(configVal, schema)
|
|
||||||
|
|
||||||
var err error
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
||||||
diff, err = provider.ReadDataDiff(legacyInfo, legacyRC)
|
return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal)
|
||||||
if err != nil {
|
|
||||||
diags = diags.Append(err)
|
|
||||||
return nil, diags.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(legacyInfo, diff)
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
*n.Output = diff
|
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 {
|
if n.OutputValue != nil {
|
||||||
*n.OutputValue = configVal
|
*n.OutputValue = change.After
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.OutputState != nil {
|
if n.OutputState != nil {
|
||||||
state := &InstanceState{}
|
state := &states.ResourceInstanceObject{
|
||||||
*n.OutputState = state
|
Value: change.After,
|
||||||
|
Status: states.ObjectReady,
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
*n.OutputState = state
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, diags.ErrWithWarnings()
|
return nil, diags.ErrWithWarnings()
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvalReadDataApply is an EvalNode implementation that executes a data
|
// EvalReadDataApply is an EvalNode implementation that executes a data
|
||||||
|
|
|
@ -198,7 +198,7 @@ func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
|
|
||||||
// TODO: Update this to use providers.Schema and populate the real
|
// TODO: Update this to use providers.Schema and populate the real
|
||||||
// schema version in the second argument to Encode below.
|
// schema version in the second argument to Encode below.
|
||||||
schema := (*n.ProviderSchema).ResourceTypes[absAddr.Resource.Resource.Type]
|
schema := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource())
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
// It shouldn't be possible to get this far in any real scenario
|
// It shouldn't be possible to get this far in any real scenario
|
||||||
// without a schema, but we might end up here in contrived tests that
|
// without a schema, but we might end up here in contrived tests that
|
||||||
|
@ -263,7 +263,7 @@ func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) {
|
||||||
|
|
||||||
// TODO: Update this to use providers.Schema and populate the real
|
// TODO: Update this to use providers.Schema and populate the real
|
||||||
// schema version in the second argument to Encode below.
|
// schema version in the second argument to Encode below.
|
||||||
schema := (*n.ProviderSchema).ResourceTypes[absAddr.Resource.Resource.Type]
|
schema := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource())
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
// It shouldn't be possible to get this far in any real scenario
|
// It shouldn't be possible to get this far in any real scenario
|
||||||
// without a schema, but we might end up here in contrived tests that
|
// without a schema, but we might end up here in contrived tests that
|
||||||
|
|
|
@ -147,7 +147,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
|
||||||
&EvalReadDataDiff{
|
&EvalReadDataDiff{
|
||||||
Addr: addr.Resource,
|
Addr: addr.Resource,
|
||||||
Config: n.Config,
|
Config: n.Config,
|
||||||
Provider: &provider,
|
ProviderAddr: n.ResolvedProvider,
|
||||||
ProviderSchema: &providerSchema,
|
ProviderSchema: &providerSchema,
|
||||||
Output: &change,
|
Output: &change,
|
||||||
OutputValue: &configVal,
|
OutputValue: &configVal,
|
||||||
|
|
|
@ -158,7 +158,7 @@ func (n *NodeApplyableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
|
||||||
&EvalReadDataDiff{
|
&EvalReadDataDiff{
|
||||||
Addr: addr.Resource,
|
Addr: addr.Resource,
|
||||||
Config: n.Config,
|
Config: n.Config,
|
||||||
Provider: &provider,
|
ProviderAddr: n.ResolvedProvider,
|
||||||
ProviderSchema: &providerSchema,
|
ProviderSchema: &providerSchema,
|
||||||
Output: &change,
|
Output: &change,
|
||||||
OutputValue: &configVal,
|
OutputValue: &configVal,
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
|
||||||
&EvalReadDataDiff{
|
&EvalReadDataDiff{
|
||||||
Addr: addr.Resource,
|
Addr: addr.Resource,
|
||||||
Config: n.Config,
|
Config: n.Config,
|
||||||
Provider: &provider,
|
ProviderAddr: n.ResolvedProvider,
|
||||||
ProviderSchema: &providerSchema,
|
ProviderSchema: &providerSchema,
|
||||||
Output: &change,
|
Output: &change,
|
||||||
OutputValue: &configVal,
|
OutputValue: &configVal,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
"github.com/hashicorp/terraform/configs/configschema"
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
"github.com/hashicorp/terraform/providers"
|
"github.com/hashicorp/terraform/providers"
|
||||||
|
@ -242,6 +243,22 @@ type ProviderSchema struct {
|
||||||
DataSources map[string]*configschema.Block
|
DataSources map[string]*configschema.Block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SchemaForResourceAddr attempts to find a schema for the mode and type from
|
||||||
|
// the given resource address. Returns nil if no such schema is available.
|
||||||
|
func (ps *ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) *configschema.Block {
|
||||||
|
var m map[string]*configschema.Block
|
||||||
|
switch addr.Mode {
|
||||||
|
case addrs.ManagedResourceMode:
|
||||||
|
m = ps.ResourceTypes
|
||||||
|
case addrs.DataResourceMode:
|
||||||
|
m = ps.DataSources
|
||||||
|
default:
|
||||||
|
// Shouldn't happen, because the above cases are comprehensive.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m[addr.Type]
|
||||||
|
}
|
||||||
|
|
||||||
// ProviderSchemaRequest is used to describe to a ResourceProvider which
|
// ProviderSchemaRequest is used to describe to a ResourceProvider which
|
||||||
// aspects of schema are required, when calling the GetSchema method.
|
// aspects of schema are required, when calling the GetSchema method.
|
||||||
type ProviderSchemaRequest struct {
|
type ProviderSchemaRequest struct {
|
||||||
|
|
Loading…
Reference in New Issue