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
|
||||
// a provider that is already initialized and retrieved.
|
||||
type EvalConfigProvider struct {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
)
|
||||
import "log"
|
||||
|
||||
// NodeDestroyableDataResourceInstance represents a resource that is "destroyable":
|
||||
// it is ready to be destroyed.
|
||||
|
@ -11,30 +8,13 @@ type NodeDestroyableDataResourceInstance struct {
|
|||
*NodeAbstractResourceInstance
|
||||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeDestroyableDataResourceInstance) EvalTree() EvalNode {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
var (
|
||||
_ GraphNodeExecutable = (*NodeDestroyableDataResourceInstance)(nil)
|
||||
)
|
||||
|
||||
var providerSchema *ProviderSchema
|
||||
// We don't need the provider, but we're calling EvalGetProvider to load the
|
||||
// schema.
|
||||
var provider providers.Interface
|
||||
|
||||
// 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,
|
||||
},
|
||||
},
|
||||
}
|
||||
// GraphNodeExecutable
|
||||
func (n *NodeDestroyableDataResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
||||
log.Printf("[TRACE] NodeDestroyableDataResourceInstance: removing state object for %s", n.Addr)
|
||||
ctx.State().SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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/dag"
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
@ -191,91 +190,97 @@ type NodeRefreshableDataResourceInstance struct {
|
|||
*NodeAbstractResourceInstance
|
||||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
|
||||
// GraphNodeExecutable
|
||||
func (n *NodeRefreshableDataResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// These variables are the state for the eval sequence below, and are
|
||||
// updated through pointers.
|
||||
var provider providers.Interface
|
||||
var providerSchema *ProviderSchema
|
||||
var change *plans.ResourceInstanceChange
|
||||
var state *states.ResourceInstanceObject
|
||||
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Addr: n.ResolvedProvider,
|
||||
Output: &provider,
|
||||
Schema: &providerSchema,
|
||||
},
|
||||
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
&EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
Output: &state,
|
||||
},
|
||||
// EvalReadState
|
||||
readStateReq := &EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
Output: &state,
|
||||
}
|
||||
_, err = readStateReq.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// EvalReadDataRefresh will _attempt_ to read the data source, but
|
||||
// may generate an incomplete planned object if the configuration
|
||||
// includes values that won't be known until apply.
|
||||
&evalReadDataRefresh{
|
||||
evalReadData{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
OutputChange: &change,
|
||||
State: &state,
|
||||
dependsOn: n.dependsOn,
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// EvalReadDataRefresh will _attempt_ to read the data source, but
|
||||
// may generate an incomplete planned object if the configuration
|
||||
// includes values that won't be known until apply.
|
||||
readDataRefreshReq := &evalReadDataRefresh{
|
||||
evalReadData{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
OutputChange: &change,
|
||||
State: &state,
|
||||
dependsOn: n.dependsOn,
|
||||
forceDependsOn: n.forceDependsOn,
|
||||
},
|
||||
}
|
||||
_, 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)
|
||||
_ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeAttachResourceState = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeEvalable = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeExecutable = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
)
|
||||
|
||||
// GraphNodeDestroyer
|
||||
|
@ -209,7 +209,7 @@ func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *addrs.AbsResourc
|
|||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
|
||||
func (n *NodeRefreshableManagedResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// Eval info is different depending on what kind of resource this is
|
||||
|
@ -217,15 +217,17 @@ func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
|
|||
case addrs.ManagedResourceMode:
|
||||
if n.instanceState == nil {
|
||||
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)
|
||||
return n.evalTreeManagedResource()
|
||||
_, err := n.evalTreeManagedResource().Eval(ctx)
|
||||
return err
|
||||
|
||||
case addrs.DataResourceMode:
|
||||
// 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).
|
||||
var dn GraphNodeEvalable
|
||||
var dn GraphNodeExecutable
|
||||
if n.Config != nil {
|
||||
dn = &NodeRefreshableDataResourceInstance{
|
||||
NodeAbstractResourceInstance: n.NodeAbstractResourceInstance,
|
||||
|
@ -236,7 +238,7 @@ func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
|
|||
}
|
||||
}
|
||||
|
||||
return dn.EvalTree()
|
||||
return dn.Execute(ctx, nil)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode))
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
|
@ -159,20 +157,3 @@ root - terraform.graphNodeRoot
|
|||
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