package terraform
import (
+ "log"
+
"github.com/hashicorp/terraform/dag"
+ "github.com/hashicorp/terraform/tfdiags"
)
// NodePlannableResource represents a resource that is "plannable":
// it is ready to be planned in order to create a diff.
type NodePlannableResource struct {
- *NodeAbstractCountResource
+ *NodeAbstractResource
+
+ // ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
+ // during graph construction, if dependencies require us to force this
+ // on regardless of what the configuration says.
+ ForceCreateBeforeDestroy *bool
+}
+
+var (
+ _ GraphNodeSubPath = (*NodePlannableResource)(nil)
+ _ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil)
+ _ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil)
+ _ GraphNodeReferenceable = (*NodePlannableResource)(nil)
+ _ GraphNodeReferencer = (*NodePlannableResource)(nil)
+ _ GraphNodeResource = (*NodePlannableResource)(nil)
+ _ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
+)
+
+// GraphNodeEvalable
+func (n *NodePlannableResource) EvalTree() EvalNode {
+ addr := n.ResourceAddr()
+ config := n.Config
+
+ if config == nil {
+ // Nothing to do, then.
+ log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr)
+ return &EvalNoop{}
+ }
+
+ // this ensures we can reference the resource even if the count is 0
+ return &EvalWriteResourceState{
+ Addr: addr.Resource,
+ Config: config,
+ ProviderAddr: n.ResolvedProvider,
+ }
+}
+
+// GraphNodeDestroyerCBD
+func (n *NodePlannableResource) CreateBeforeDestroy() bool {
+ if n.ForceCreateBeforeDestroy != nil {
+ return *n.ForceCreateBeforeDestroy
+ }
+
+ // If we have no config, we just assume no
+ if n.Config == nil || n.Config.Managed == nil {
+ return false
+ }
+
+ return n.Config.Managed.CreateBeforeDestroy
+}
+
+// GraphNodeDestroyerCBD
+func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error {
+ n.ForceCreateBeforeDestroy = &v
+ return nil
}
// GraphNodeDynamicExpandable
func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
- // Grab the state which we read
- state, lock := ctx.State()
- lock.RLock()
- defer lock.RUnlock()
-
- // Expand the resource count which must be available by now from EvalTree
- count, err := n.Config.Count()
- if err != nil {
- return nil, err
+ var diags tfdiags.Diagnostics
+
+ count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
+ diags = diags.Append(countDiags)
+ if countDiags.HasErrors() {
+ return nil, diags.Err()
}
+ // Next we need to potentially rename an instance address in the state
+ // if we're transitioning whether "count" is set at all.
+ fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
+
+ // Our graph transformers require access to the full state, so we'll
+ // temporarily lock it while we work on this.
+ state := ctx.State().Lock()
+ defer ctx.State().Unlock()
+
// The concrete resource factory we'll use
- concreteResource := func(a *NodeAbstractResource) dag.Vertex {
+ concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
+ a.Schema = n.Schema
+ a.ProvisionerSchemas = n.ProvisionerSchemas
return &NodePlannableResourceInstance{
- NodeAbstractResource: a,
+ NodeAbstractResourceInstance: a,
+
+ // By the time we're walking, we've figured out whether we need
+ // to force on CreateBeforeDestroy due to dependencies on other
+ // nodes that have it.
+ ForceCreateBeforeDestroy: n.CreateBeforeDestroy(),
}
}
- // The concrete resource factory we'll use for oprhans
- concreteResourceOrphan := func(a *NodeAbstractResource) dag.Vertex {
+ // The concrete resource factory we'll use for orphans
+ concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
+ a.Schema = n.Schema
+ a.ProvisionerSchemas = n.ProvisionerSchemas
- return &NodePlannableResourceOrphan{
- NodeAbstractResource: a,
+ return &NodePlannableResourceInstanceOrphan{
+ NodeAbstractResourceInstance: a,
}
}
// Expand the count.
&ResourceCountTransformer{
Concrete: concreteResource,
+ Schema: n.Schema,
Count: count,
Addr: n.ResourceAddr(),
},
&AttachStateTransformer{State: state},
// Targeting
- &TargetsTransformer{ParsedTargets: n.Targets},
+ &TargetsTransformer{Targets: n.Targets},
// Connect references so ordering is correct
&ReferenceTransformer{},
Validate: true,
Name: "NodePlannableResource",
}
- return b.Build(ctx.Path())
+ graph, diags := b.Build(ctx.Path())
+ return graph, diags.ErrWithWarnings()
}