terraform: begin NodePlannableResource
This commit is contained in:
parent
dbac0785bc
commit
d7aa59be3c
|
@ -487,7 +487,7 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
c.diffLock.Unlock()
|
c.diffLock.Unlock()
|
||||||
|
|
||||||
// Used throughout below
|
// Used throughout below
|
||||||
X_newApply := experiment.Enabled(experiment.X_newDestroy)
|
X_newApply := experiment.Enabled(experiment.X_newApply)
|
||||||
X_newDestroy := experiment.Enabled(experiment.X_newDestroy)
|
X_newDestroy := experiment.Enabled(experiment.X_newDestroy)
|
||||||
newGraphEnabled := (c.destroy && X_newDestroy) || (!c.destroy && X_newApply)
|
newGraphEnabled := (c.destroy && X_newDestroy) || (!c.destroy && X_newApply)
|
||||||
|
|
||||||
|
@ -515,8 +515,11 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
Targets: c.targets,
|
Targets: c.targets,
|
||||||
}).Build(RootModulePath)
|
}).Build(RootModulePath)
|
||||||
} else {
|
} else {
|
||||||
// TODO: new plan graph when its ready
|
newGraph, err = (&PlanGraphBuilder{
|
||||||
newGraph = nil
|
Module: c.module,
|
||||||
|
State: c.state,
|
||||||
|
Providers: c.components.ResourceProviders(),
|
||||||
|
}).Build(RootModulePath)
|
||||||
}
|
}
|
||||||
if err != nil && !newGraphEnabled {
|
if err != nil && !newGraphEnabled {
|
||||||
// If we had an error graphing but we're not using this graph, just
|
// If we had an error graphing but we're not using this graph, just
|
||||||
|
@ -554,6 +557,9 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
shadow = nil
|
shadow = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove when we're ready
|
||||||
|
shadow = nil
|
||||||
|
|
||||||
// Do the walk
|
// Do the walk
|
||||||
walker, err := c.walk(real, shadow, operation)
|
walker, err := c.walk(real, shadow, operation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -574,7 +580,7 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
|
|
||||||
// We don't do the reverification during the new destroy plan because
|
// We don't do the reverification during the new destroy plan because
|
||||||
// it will use a different apply process.
|
// it will use a different apply process.
|
||||||
if !(c.destroy && X_newDestroy) {
|
if !newGraphEnabled {
|
||||||
// Now that we have a diff, we can build the exact graph that Apply will use
|
// Now that we have a diff, we can build the exact graph that Apply will use
|
||||||
// and catch any possible cycles during the Plan phase.
|
// and catch any possible cycles during the Plan phase.
|
||||||
if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
|
if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContext2Plan(t *testing.T) {
|
func TestContext2Plan_basic(t *testing.T) {
|
||||||
m := testModule(t, "plan-good")
|
m := testModule(t, "plan-good")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
|
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
|
||||||
return &NodeApplyableResource{
|
return &NodePlannableResource{
|
||||||
NodeAbstractResource: a,
|
NodeAbstractResource: a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
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 {
|
||||||
|
*NodeAbstractResource
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue