mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
terraform: EvalNode removal, continued
This commit continues the overall EvalNode removal project. Something to note: the NodeRefreshableDataResourceInstance's Execute() function is intentionally refactored in the bare minimum, hardly-a-refactor style, because we have another ongoing project which aims to remove NodeRefreshable*s. It is not worth the effort at this time. We may revisit this decision in the future.
This commit is contained in:
parent
df6f3fa6de
commit
8a4b2ab817
@ -46,6 +46,22 @@ func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetProvider returns the providers interface and schema for a given provider.
|
||||||
|
func GetProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Interface, *ProviderSchema, error) {
|
||||||
|
if addr.Provider.Type == "" {
|
||||||
|
// Should never happen
|
||||||
|
panic("EvalGetProvider used with uninitialized provider configuration address")
|
||||||
|
}
|
||||||
|
provider := ctx.Provider(addr)
|
||||||
|
if provider == nil {
|
||||||
|
return nil, &ProviderSchema{}, fmt.Errorf("provider %s not initialized", addr)
|
||||||
|
}
|
||||||
|
// Not all callers require a schema, so we will leave checking for a nil
|
||||||
|
// schema to the callers.
|
||||||
|
schema := ctx.ProviderSchema(addr)
|
||||||
|
return provider, schema, nil
|
||||||
|
}
|
||||||
|
|
||||||
// EvalConfigProvider is an EvalNode implementation that configures
|
// EvalConfigProvider is an EvalNode implementation that configures
|
||||||
// a provider that is already initialized and retrieved.
|
// a provider that is already initialized and retrieved.
|
||||||
type EvalConfigProvider struct {
|
type EvalConfigProvider struct {
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import "log"
|
||||||
"github.com/hashicorp/terraform/providers"
|
|
||||||
"github.com/hashicorp/terraform/states"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NodeDestroyableDataResourceInstance represents a resource that is "destroyable":
|
// NodeDestroyableDataResourceInstance represents a resource that is "destroyable":
|
||||||
// it is ready to be destroyed.
|
// it is ready to be destroyed.
|
||||||
@ -11,30 +8,13 @@ type NodeDestroyableDataResourceInstance struct {
|
|||||||
*NodeAbstractResourceInstance
|
*NodeAbstractResourceInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
var (
|
||||||
func (n *NodeDestroyableDataResourceInstance) EvalTree() EvalNode {
|
_ GraphNodeExecutable = (*NodeDestroyableDataResourceInstance)(nil)
|
||||||
addr := n.ResourceInstanceAddr()
|
)
|
||||||
|
|
||||||
var providerSchema *ProviderSchema
|
// GraphNodeExecutable
|
||||||
// We don't need the provider, but we're calling EvalGetProvider to load the
|
func (n *NodeDestroyableDataResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
||||||
// schema.
|
log.Printf("[TRACE] NodeDestroyableDataResourceInstance: removing state object for %s", n.Addr)
|
||||||
var provider providers.Interface
|
ctx.State().SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider)
|
||||||
|
return nil
|
||||||
// Just destroy it.
|
|
||||||
var state *states.ResourceInstanceObject
|
|
||||||
return &EvalSequence{
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalGetProvider{
|
|
||||||
Addr: n.ResolvedProvider,
|
|
||||||
Output: &provider,
|
|
||||||
Schema: &providerSchema,
|
|
||||||
},
|
|
||||||
&EvalWriteState{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
State: &state,
|
|
||||||
ProviderAddr: n.ResolvedProvider,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
48
terraform/node_data_destroy_test.go
Normal file
48
terraform/node_data_destroy_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
"github.com/hashicorp/terraform/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNodeDataDestroyExecute(t *testing.T) {
|
||||||
|
state := states.NewState()
|
||||||
|
state.Module(addrs.RootModuleInstance).SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.DataResourceMode,
|
||||||
|
Type: "test_instance",
|
||||||
|
Name: "foo",
|
||||||
|
}.Instance(addrs.NoKey),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
AttrsJSON: []byte(`{"dynamic":{"type":"string","value":"hello"}}`),
|
||||||
|
},
|
||||||
|
addrs.AbsProviderConfig{
|
||||||
|
Provider: addrs.NewDefaultProvider("test"),
|
||||||
|
Module: addrs.RootModule,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
ctx := &MockEvalContext{
|
||||||
|
StateState: state.SyncWrapper(),
|
||||||
|
}
|
||||||
|
|
||||||
|
node := NodeDestroyableDataResourceInstance{&NodeAbstractResourceInstance{
|
||||||
|
Addr: addrs.Resource{
|
||||||
|
Mode: addrs.DataResourceMode,
|
||||||
|
Type: "test_instance",
|
||||||
|
Name: "foo",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
|
}}
|
||||||
|
|
||||||
|
err := node.Execute(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify resource removed from state
|
||||||
|
if state.HasResources() {
|
||||||
|
t.Fatal("resources still in state after NodeDataDestroy.Execute")
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import (
|
|||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/dag"
|
"github.com/hashicorp/terraform/dag"
|
||||||
"github.com/hashicorp/terraform/plans"
|
"github.com/hashicorp/terraform/plans"
|
||||||
"github.com/hashicorp/terraform/providers"
|
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
)
|
)
|
||||||
@ -191,91 +190,97 @@ type NodeRefreshableDataResourceInstance struct {
|
|||||||
*NodeAbstractResourceInstance
|
*NodeAbstractResourceInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
// GraphNodeExecutable
|
||||||
func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
|
func (n *NodeRefreshableDataResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
||||||
addr := n.ResourceInstanceAddr()
|
addr := n.ResourceInstanceAddr()
|
||||||
|
|
||||||
// These variables are the state for the eval sequence below, and are
|
// These variables are the state for the eval sequence below, and are
|
||||||
// updated through pointers.
|
// updated through pointers.
|
||||||
var provider providers.Interface
|
|
||||||
var providerSchema *ProviderSchema
|
|
||||||
var change *plans.ResourceInstanceChange
|
var change *plans.ResourceInstanceChange
|
||||||
var state *states.ResourceInstanceObject
|
var state *states.ResourceInstanceObject
|
||||||
|
|
||||||
return &EvalSequence{
|
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||||
Nodes: []EvalNode{
|
if err != nil {
|
||||||
&EvalGetProvider{
|
return err
|
||||||
Addr: n.ResolvedProvider,
|
}
|
||||||
Output: &provider,
|
|
||||||
Schema: &providerSchema,
|
|
||||||
},
|
|
||||||
|
|
||||||
&EvalReadState{
|
// EvalReadState
|
||||||
Addr: addr.Resource,
|
readStateReq := &EvalReadState{
|
||||||
Provider: &provider,
|
Addr: addr.Resource,
|
||||||
ProviderSchema: &providerSchema,
|
Provider: &provider,
|
||||||
Output: &state,
|
ProviderSchema: &providerSchema,
|
||||||
},
|
Output: &state,
|
||||||
|
}
|
||||||
|
_, err = readStateReq.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// EvalReadDataRefresh will _attempt_ to read the data source, but
|
// EvalReadDataRefresh will _attempt_ to read the data source, but
|
||||||
// may generate an incomplete planned object if the configuration
|
// may generate an incomplete planned object if the configuration
|
||||||
// includes values that won't be known until apply.
|
// includes values that won't be known until apply.
|
||||||
&evalReadDataRefresh{
|
readDataRefreshReq := &evalReadDataRefresh{
|
||||||
evalReadData{
|
evalReadData{
|
||||||
Addr: addr.Resource,
|
Addr: addr.Resource,
|
||||||
Config: n.Config,
|
Config: n.Config,
|
||||||
Provider: &provider,
|
Provider: &provider,
|
||||||
ProviderAddr: n.ResolvedProvider,
|
ProviderAddr: n.ResolvedProvider,
|
||||||
ProviderMetas: n.ProviderMetas,
|
ProviderMetas: n.ProviderMetas,
|
||||||
ProviderSchema: &providerSchema,
|
ProviderSchema: &providerSchema,
|
||||||
OutputChange: &change,
|
OutputChange: &change,
|
||||||
State: &state,
|
State: &state,
|
||||||
dependsOn: n.dependsOn,
|
dependsOn: n.dependsOn,
|
||||||
forceDependsOn: n.forceDependsOn,
|
forceDependsOn: n.forceDependsOn,
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&EvalIf{
|
|
||||||
If: func(ctx EvalContext) (bool, error) {
|
|
||||||
return change == nil, nil
|
|
||||||
|
|
||||||
},
|
|
||||||
Then: &EvalSequence{
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalWriteState{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
ProviderAddr: n.ResolvedProvider,
|
|
||||||
State: &state,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
},
|
|
||||||
&EvalUpdateStateHook{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Else: &EvalSequence{
|
|
||||||
// We can't deal with this yet, so we'll repeat this step
|
|
||||||
// during the plan walk to produce a planned change to read
|
|
||||||
// this during the apply walk. However, we do still need to
|
|
||||||
// save the generated change and partial state so that
|
|
||||||
// results from it can be included in other data resources
|
|
||||||
// or provider configurations during the refresh walk.
|
|
||||||
// (The planned object we save in the state here will be
|
|
||||||
// pruned out at the end of the refresh walk, returning
|
|
||||||
// it back to being unset again for subsequent walks.)
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalWriteDiff{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
Change: &change,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
},
|
|
||||||
&EvalWriteState{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
ProviderAddr: n.ResolvedProvider,
|
|
||||||
State: &state,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
_, err = readDataRefreshReq.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if change == nil {
|
||||||
|
// EvalWriteState
|
||||||
|
writeStateRequest := EvalWriteState{
|
||||||
|
Addr: addr.Resource,
|
||||||
|
ProviderAddr: n.ResolvedProvider,
|
||||||
|
State: &state,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
}
|
||||||
|
_, err := writeStateRequest.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalUpdateStateHook
|
||||||
|
updateStateHookReq := EvalUpdateStateHook{}
|
||||||
|
_, err = updateStateHookReq.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// EvalWriteDiff
|
||||||
|
writeDiffReq := &EvalWriteDiff{
|
||||||
|
Addr: addr.Resource,
|
||||||
|
Change: &change,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
}
|
||||||
|
_, err = writeDiffReq.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// EvalWriteState
|
||||||
|
writeStateRequest := EvalWriteState{
|
||||||
|
Addr: addr.Resource,
|
||||||
|
ProviderAddr: n.ResolvedProvider,
|
||||||
|
State: &state,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
}
|
||||||
|
_, err := writeStateRequest.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ var (
|
|||||||
_ GraphNodeResourceInstance = (*NodeRefreshableManagedResourceInstance)(nil)
|
_ GraphNodeResourceInstance = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||||
_ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResourceInstance)(nil)
|
_ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||||
_ GraphNodeAttachResourceState = (*NodeRefreshableManagedResourceInstance)(nil)
|
_ GraphNodeAttachResourceState = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||||
_ GraphNodeEvalable = (*NodeRefreshableManagedResourceInstance)(nil)
|
_ GraphNodeExecutable = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
// GraphNodeDestroyer
|
// GraphNodeDestroyer
|
||||||
@ -209,7 +209,7 @@ func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *addrs.AbsResourc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
// GraphNodeEvalable
|
||||||
func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
|
func (n *NodeRefreshableManagedResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
||||||
addr := n.ResourceInstanceAddr()
|
addr := n.ResourceInstanceAddr()
|
||||||
|
|
||||||
// Eval info is different depending on what kind of resource this is
|
// Eval info is different depending on what kind of resource this is
|
||||||
@ -217,15 +217,17 @@ func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
|
|||||||
case addrs.ManagedResourceMode:
|
case addrs.ManagedResourceMode:
|
||||||
if n.instanceState == nil {
|
if n.instanceState == nil {
|
||||||
log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s has no existing state to refresh", addr)
|
log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s has no existing state to refresh", addr)
|
||||||
return n.evalTreeManagedResourceNoState()
|
_, err := n.evalTreeManagedResourceNoState().Eval(ctx)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s will be refreshed", addr)
|
log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s will be refreshed", addr)
|
||||||
return n.evalTreeManagedResource()
|
_, err := n.evalTreeManagedResource().Eval(ctx)
|
||||||
|
return err
|
||||||
|
|
||||||
case addrs.DataResourceMode:
|
case addrs.DataResourceMode:
|
||||||
// Get the data source node. If we don't have a configuration
|
// Get the data source node. If we don't have a configuration
|
||||||
// then it is an orphan so we destroy it (remove it from the state).
|
// then it is an orphan so we destroy it (remove it from the state).
|
||||||
var dn GraphNodeEvalable
|
var dn GraphNodeExecutable
|
||||||
if n.Config != nil {
|
if n.Config != nil {
|
||||||
dn = &NodeRefreshableDataResourceInstance{
|
dn = &NodeRefreshableDataResourceInstance{
|
||||||
NodeAbstractResourceInstance: n.NodeAbstractResourceInstance,
|
NodeAbstractResourceInstance: n.NodeAbstractResourceInstance,
|
||||||
@ -236,7 +238,7 @@ func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dn.EvalTree()
|
return dn.Execute(ctx, nil)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode))
|
panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode))
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
@ -159,20 +157,3 @@ root - terraform.graphNodeRoot
|
|||||||
t.Fatalf("Expected:\n%s\nGot:\n%s", expected, actual)
|
t.Fatalf("Expected:\n%s\nGot:\n%s", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeRefreshableManagedResourceEvalTree_scaleOut(t *testing.T) {
|
|
||||||
m := testModule(t, "refresh-resource-scale-inout")
|
|
||||||
|
|
||||||
n := &NodeRefreshableManagedResourceInstance{
|
|
||||||
NodeAbstractResourceInstance: NewNodeAbstractResourceInstance(addrs.RootModuleInstance.Resource(addrs.ManagedResourceMode, "aws_instance", "foo").Instance(addrs.IntKey(2))),
|
|
||||||
}
|
|
||||||
|
|
||||||
n.AttachResourceConfig(m.Module.ManagedResources["aws_instance.foo"])
|
|
||||||
|
|
||||||
actual := n.EvalTree()
|
|
||||||
expected := n.evalTreeManagedResourceNoState()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, actual) {
|
|
||||||
t.Fatalf("Expected:\n\n%s\nGot:\n\n%s\n", spew.Sdump(expected), spew.Sdump(actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user