terraform: ResourceTransformer to ResourceTransformerOld

This commit is contained in:
Mitchell Hashimoto 2016-11-06 10:07:17 -08:00
parent d7aa59be3c
commit 4cdaf6f687
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
5 changed files with 241 additions and 204 deletions

View File

@ -156,7 +156,7 @@ func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error)
steps := make([]GraphTransformer, 0, 5)
// Expand counts.
steps = append(steps, &ResourceCountTransformer{
steps = append(steps, &ResourceCountTransformerOld{
Resource: n.Resource,
Destroy: n.Destroy,
Targets: n.Targets,

View File

@ -1,11 +1,5 @@
package terraform
import (
"fmt"
"github.com/hashicorp/terraform/config"
)
// NodePlannableResource represents a resource that is "plannable":
// it is ready to be planned in order to create a diff.
type NodePlannableResource struct {
@ -14,194 +8,41 @@ type NodePlannableResource struct {
// GraphNodeEvalable
func (n *NodePlannableResource) EvalTree() EvalNode {
addr := n.NodeAbstractResource.Addr
// stateId is the ID to put into the state
stateId := addr.stateId()
if addr.Index > -1 {
stateId = fmt.Sprintf("%s.%d", stateId, addr.Index)
}
// Build the instance info. More of this will be populated during eval
info := &InstanceInfo{
Id: stateId,
Type: addr.Type,
}
// Build the resource for eval
resource := &Resource{
Name: addr.Name,
Type: addr.Type,
CountIndex: addr.Index,
}
if resource.CountIndex < 0 {
resource.CountIndex = 0
}
// Determine the dependencies for the state. We use some older
// code for this that we've used for a long time.
var stateDeps []string
{
oldN := &graphNodeExpandedResource{Resource: n.Config}
stateDeps = oldN.StateDependencies()
}
// Eval info is different depending on what kind of resource this is
switch n.Config.Mode {
case config.ManagedResourceMode:
return n.evalTreeManagedResource(
stateId, info, resource, stateDeps,
)
case config.DataResourceMode:
return n.evalTreeDataResource(
stateId, info, resource, stateDeps)
default:
panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
}
}
func (n *NodePlannableResource) evalTreeDataResource(
stateId string, info *InstanceInfo,
resource *Resource, stateDeps []string) EvalNode {
var provider ResourceProvider
var config *ResourceConfig
var diff *InstanceDiff
var state *InstanceState
return &EvalSequence{
Nodes: []EvalNode{
// Get the saved diff for apply
&EvalReadDiff{
Name: stateId,
Diff: &diff,
},
// Stop here if we don't actually have a diff
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if diff == nil {
return true, EvalEarlyExitError{}
}
if diff.GetAttributesLen() == 0 {
return true, EvalEarlyExitError{}
}
return true, nil
},
Then: EvalNoop{},
},
// We need to re-interpolate the config here, rather than
// just using the diff's values directly, because we've
// potentially learned more variable values during the
// apply pass that weren't known when the diff was produced.
&EvalInterpolate{
Config: n.Config.RawConfig.Copy(),
Resource: resource,
Output: &config,
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Output: &provider,
},
// Make a new diff with our newly-interpolated config.
&EvalReadDataDiff{
Info: info,
Config: &config,
Previous: &diff,
Provider: &provider,
Output: &diff,
},
&EvalReadDataApply{
Info: info,
Diff: &diff,
Provider: &provider,
Output: &state,
},
&EvalWriteState{
Name: stateId,
ResourceType: n.Config.Type,
Provider: n.Config.Provider,
Dependencies: stateDeps,
State: &state,
},
// Clear the diff now that we've applied it, so
// later nodes won't see a diff that's now a no-op.
&EvalWriteDiff{
Name: stateId,
Diff: nil,
},
&EvalUpdateStateHook{},
// The EvalTree for a plannable resource primarily involves
// interpolating the count since it can contain variables
// we only just received access to.
//
// With the interpolated count, we can then DynamicExpand
// into the proper number of instances.
&EvalInterpolate{Config: n.Config.RawCount},
},
}
}
func (n *NodePlannableResource) evalTreeManagedResource(
stateId string, info *InstanceInfo,
resource *Resource, stateDeps []string) EvalNode {
// Declare a bunch of variables that are used for state during
// evaluation. Most of this are written to by-address below.
var provider ResourceProvider
var diff *InstanceDiff
var state *InstanceState
var resourceConfig *ResourceConfig
/*
// GraphNodeDynamicExpandable
func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
state, lock := ctx.State()
lock.RLock()
defer lock.RUnlock()
return &EvalSequence{
Nodes: []EvalNode{
&EvalInterpolate{
Config: n.Config.RawConfig.Copy(),
Resource: resource,
Output: &resourceConfig,
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Output: &provider,
},
// Re-run validation to catch any errors we missed, e.g. type
// mismatches on computed values.
&EvalValidateResource{
Provider: &provider,
Config: &resourceConfig,
ResourceName: n.Config.Name,
ResourceType: n.Config.Type,
ResourceMode: n.Config.Mode,
IgnoreWarnings: true,
},
&EvalReadState{
Name: stateId,
Output: &state,
},
&EvalDiff{
Info: info,
Config: &resourceConfig,
Resource: n.Config,
Provider: &provider,
State: &state,
OutputDiff: &diff,
OutputState: &state,
},
&EvalCheckPreventDestroy{
Resource: n.Config,
Diff: &diff,
},
&EvalWriteState{
Name: stateId,
ResourceType: n.Config.Type,
Provider: n.Config.Provider,
Dependencies: stateDeps,
State: &state,
},
&EvalWriteDiff{
Name: stateId,
Diff: &diff,
},
},
}
// Start creating the steps
steps := make([]GraphTransformer, 0, 5)
// Expand counts.
steps = append(steps, &ResourceCountTransformer{
Resource: n.Resource,
Destroy: n.Destroy,
Targets: n.Targets,
})
// Always end with the root being added
steps = append(steps, &RootTransformer{})
// Build the graph
b := &BasicGraphBuilder{Steps: steps, Validate: true}
return b.Build(ctx.Path())
}
*/

View File

@ -0,0 +1,196 @@
package terraform
import (
"fmt"
"github.com/hashicorp/terraform/config"
)
// NodePlannableResourceInstance represents a _single_ resource
// instance that is plannable. This means this represents a single
// count index, for example.
type NodePlannableResourceInstance struct {
*NodeAbstractResource
}
// GraphNodeEvalable
func (n *NodePlannableResourceInstance) EvalTree() EvalNode {
addr := n.NodeAbstractResource.Addr
// stateId is the ID to put into the state
stateId := addr.stateId()
if addr.Index > -1 {
stateId = fmt.Sprintf("%s.%d", stateId, addr.Index)
}
// Build the instance info. More of this will be populated during eval
info := &InstanceInfo{
Id: stateId,
Type: addr.Type,
}
// Build the resource for eval
resource := &Resource{
Name: addr.Name,
Type: addr.Type,
CountIndex: addr.Index,
}
if resource.CountIndex < 0 {
resource.CountIndex = 0
}
// Determine the dependencies for the state. We use some older
// code for this that we've used for a long time.
var stateDeps []string
{
oldN := &graphNodeExpandedResource{Resource: n.Config}
stateDeps = oldN.StateDependencies()
}
// Eval info is different depending on what kind of resource this is
switch n.Config.Mode {
case config.ManagedResourceMode:
return n.evalTreeManagedResource(
stateId, info, resource, stateDeps,
)
case config.DataResourceMode:
return n.evalTreeDataResource(
stateId, info, resource, stateDeps)
default:
panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
}
}
func (n *NodePlannableResourceInstance) evalTreeDataResource(
stateId string, info *InstanceInfo,
resource *Resource, stateDeps []string) EvalNode {
var provider ResourceProvider
var config *ResourceConfig
var diff *InstanceDiff
var state *InstanceState
return &EvalSequence{
Nodes: []EvalNode{
&EvalReadState{
Name: stateId,
Output: &state,
},
// We need to re-interpolate the config here because some
// of the attributes may have become computed during
// earlier planning, due to other resources having
// "requires new resource" diffs.
&EvalInterpolate{
Config: n.Config.RawConfig.Copy(),
Resource: resource,
Output: &config,
},
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
computed := config.ComputedKeys != nil && len(config.ComputedKeys) > 0
// If the configuration is complete and we
// already have a state then we don't need to
// do any further work during apply, because we
// already populated the state during refresh.
if !computed && state != nil {
return true, EvalEarlyExitError{}
}
return true, nil
},
Then: EvalNoop{},
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Output: &provider,
},
&EvalReadDataDiff{
Info: info,
Config: &config,
Provider: &provider,
Output: &diff,
OutputState: &state,
},
&EvalWriteState{
Name: stateId,
ResourceType: n.Config.Type,
Provider: n.Config.Provider,
Dependencies: stateDeps,
State: &state,
},
&EvalWriteDiff{
Name: stateId,
Diff: &diff,
},
},
}
}
func (n *NodePlannableResourceInstance) evalTreeManagedResource(
stateId string, info *InstanceInfo,
resource *Resource, stateDeps []string) EvalNode {
// Declare a bunch of variables that are used for state during
// evaluation. Most of this are written to by-address below.
var provider ResourceProvider
var diff *InstanceDiff
var state *InstanceState
var resourceConfig *ResourceConfig
return &EvalSequence{
Nodes: []EvalNode{
&EvalInterpolate{
Config: n.Config.RawConfig.Copy(),
Resource: resource,
Output: &resourceConfig,
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Output: &provider,
},
// Re-run validation to catch any errors we missed, e.g. type
// mismatches on computed values.
&EvalValidateResource{
Provider: &provider,
Config: &resourceConfig,
ResourceName: n.Config.Name,
ResourceType: n.Config.Type,
ResourceMode: n.Config.Mode,
IgnoreWarnings: true,
},
&EvalReadState{
Name: stateId,
Output: &state,
},
&EvalDiff{
Info: info,
Config: &resourceConfig,
Resource: n.Config,
Provider: &provider,
State: &state,
OutputDiff: &diff,
OutputState: &state,
},
&EvalCheckPreventDestroy{
Resource: n.Config,
Diff: &diff,
},
&EvalWriteState{
Name: stateId,
ResourceType: n.Config.Type,
Provider: n.Config.Provider,
Dependencies: stateDeps,
State: &state,
},
&EvalWriteDiff{
Name: stateId,
Diff: &diff,
},
},
}
}

View File

@ -8,15 +8,15 @@ import (
"github.com/hashicorp/terraform/dag"
)
// ResourceCountTransformer is a GraphTransformer that expands the count
// ResourceCountTransformerOld is a GraphTransformer that expands the count
// out for a specific resource.
type ResourceCountTransformer struct {
type ResourceCountTransformerOld struct {
Resource *config.Resource
Destroy bool
Targets []ResourceAddress
}
func (t *ResourceCountTransformer) Transform(g *Graph) error {
func (t *ResourceCountTransformerOld) Transform(g *Graph) error {
// Expand the resource count
count, err := t.Resource.Count()
if err != nil {
@ -72,7 +72,7 @@ func (t *ResourceCountTransformer) Transform(g *Graph) error {
return nil
}
func (t *ResourceCountTransformer) nodeIsTargeted(node dag.Vertex) bool {
func (t *ResourceCountTransformerOld) nodeIsTargeted(node dag.Vertex) bool {
// no targets specified, everything stays in the graph
if len(t.Targets) == 0 {
return true

View File

@ -5,64 +5,64 @@ import (
"testing"
)
func TestResourceCountTransformer(t *testing.T) {
func TestResourceCountTransformerOld(t *testing.T) {
cfg := testModule(t, "transform-resource-count-basic").Config()
resource := cfg.Resources[0]
g := Graph{Path: RootModulePath}
{
tf := &ResourceCountTransformer{Resource: resource}
tf := &ResourceCountTransformerOld{Resource: resource}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testResourceCountTransformStr)
expected := strings.TrimSpace(testResourceCountTransformOldStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
func TestResourceCountTransformer_countNegative(t *testing.T) {
func TestResourceCountTransformerOld_countNegative(t *testing.T) {
cfg := testModule(t, "transform-resource-count-negative").Config()
resource := cfg.Resources[0]
g := Graph{Path: RootModulePath}
{
tf := &ResourceCountTransformer{Resource: resource}
tf := &ResourceCountTransformerOld{Resource: resource}
if err := tf.Transform(&g); err == nil {
t.Fatal("should error")
}
}
}
func TestResourceCountTransformer_deps(t *testing.T) {
func TestResourceCountTransformerOld_deps(t *testing.T) {
cfg := testModule(t, "transform-resource-count-deps").Config()
resource := cfg.Resources[0]
g := Graph{Path: RootModulePath}
{
tf := &ResourceCountTransformer{Resource: resource}
tf := &ResourceCountTransformerOld{Resource: resource}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testResourceCountTransformDepsStr)
expected := strings.TrimSpace(testResourceCountTransformOldDepsStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
const testResourceCountTransformStr = `
const testResourceCountTransformOldStr = `
aws_instance.foo #0
aws_instance.foo #1
aws_instance.foo #2
`
const testResourceCountTransformDepsStr = `
const testResourceCountTransformOldDepsStr = `
aws_instance.foo #0
aws_instance.foo #1
aws_instance.foo #0