]>
Commit | Line | Data |
---|---|---|
1 | package terraform | |
2 | ||
3 | import ( | |
4 | "log" | |
5 | ||
6 | "github.com/hashicorp/terraform/dag" | |
7 | "github.com/hashicorp/terraform/tfdiags" | |
8 | ) | |
9 | ||
10 | // NodePlannableResource represents a resource that is "plannable": | |
11 | // it is ready to be planned in order to create a diff. | |
12 | type NodePlannableResource struct { | |
13 | *NodeAbstractResource | |
14 | ||
15 | // ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD | |
16 | // during graph construction, if dependencies require us to force this | |
17 | // on regardless of what the configuration says. | |
18 | ForceCreateBeforeDestroy *bool | |
19 | } | |
20 | ||
21 | var ( | |
22 | _ GraphNodeSubPath = (*NodePlannableResource)(nil) | |
23 | _ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil) | |
24 | _ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil) | |
25 | _ GraphNodeReferenceable = (*NodePlannableResource)(nil) | |
26 | _ GraphNodeReferencer = (*NodePlannableResource)(nil) | |
27 | _ GraphNodeResource = (*NodePlannableResource)(nil) | |
28 | _ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil) | |
29 | ) | |
30 | ||
31 | // GraphNodeEvalable | |
32 | func (n *NodePlannableResource) EvalTree() EvalNode { | |
33 | addr := n.ResourceAddr() | |
34 | config := n.Config | |
35 | ||
36 | if config == nil { | |
37 | // Nothing to do, then. | |
38 | log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr) | |
39 | return &EvalNoop{} | |
40 | } | |
41 | ||
42 | // this ensures we can reference the resource even if the count is 0 | |
43 | return &EvalWriteResourceState{ | |
44 | Addr: addr.Resource, | |
45 | Config: config, | |
46 | ProviderAddr: n.ResolvedProvider, | |
47 | } | |
48 | } | |
49 | ||
50 | // GraphNodeDestroyerCBD | |
51 | func (n *NodePlannableResource) CreateBeforeDestroy() bool { | |
52 | if n.ForceCreateBeforeDestroy != nil { | |
53 | return *n.ForceCreateBeforeDestroy | |
54 | } | |
55 | ||
56 | // If we have no config, we just assume no | |
57 | if n.Config == nil || n.Config.Managed == nil { | |
58 | return false | |
59 | } | |
60 | ||
61 | return n.Config.Managed.CreateBeforeDestroy | |
62 | } | |
63 | ||
64 | // GraphNodeDestroyerCBD | |
65 | func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error { | |
66 | n.ForceCreateBeforeDestroy = &v | |
67 | return nil | |
68 | } | |
69 | ||
70 | // GraphNodeDynamicExpandable | |
71 | func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { | |
72 | var diags tfdiags.Diagnostics | |
73 | ||
74 | count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx) | |
75 | diags = diags.Append(countDiags) | |
76 | if countDiags.HasErrors() { | |
77 | return nil, diags.Err() | |
78 | } | |
79 | ||
80 | // Next we need to potentially rename an instance address in the state | |
81 | // if we're transitioning whether "count" is set at all. | |
82 | fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1) | |
83 | ||
84 | // Our graph transformers require access to the full state, so we'll | |
85 | // temporarily lock it while we work on this. | |
86 | state := ctx.State().Lock() | |
87 | defer ctx.State().Unlock() | |
88 | ||
89 | // The concrete resource factory we'll use | |
90 | concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { | |
91 | // Add the config and state since we don't do that via transforms | |
92 | a.Config = n.Config | |
93 | a.ResolvedProvider = n.ResolvedProvider | |
94 | a.Schema = n.Schema | |
95 | a.ProvisionerSchemas = n.ProvisionerSchemas | |
96 | ||
97 | return &NodePlannableResourceInstance{ | |
98 | NodeAbstractResourceInstance: a, | |
99 | ||
100 | // By the time we're walking, we've figured out whether we need | |
101 | // to force on CreateBeforeDestroy due to dependencies on other | |
102 | // nodes that have it. | |
103 | ForceCreateBeforeDestroy: n.CreateBeforeDestroy(), | |
104 | } | |
105 | } | |
106 | ||
107 | // The concrete resource factory we'll use for orphans | |
108 | concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex { | |
109 | // Add the config and state since we don't do that via transforms | |
110 | a.Config = n.Config | |
111 | a.ResolvedProvider = n.ResolvedProvider | |
112 | a.Schema = n.Schema | |
113 | a.ProvisionerSchemas = n.ProvisionerSchemas | |
114 | ||
115 | return &NodePlannableResourceInstanceOrphan{ | |
116 | NodeAbstractResourceInstance: a, | |
117 | } | |
118 | } | |
119 | ||
120 | // Start creating the steps | |
121 | steps := []GraphTransformer{ | |
122 | // Expand the count. | |
123 | &ResourceCountTransformer{ | |
124 | Concrete: concreteResource, | |
125 | Schema: n.Schema, | |
126 | Count: count, | |
127 | Addr: n.ResourceAddr(), | |
128 | }, | |
129 | ||
130 | // Add the count orphans | |
131 | &OrphanResourceCountTransformer{ | |
132 | Concrete: concreteResourceOrphan, | |
133 | Count: count, | |
134 | Addr: n.ResourceAddr(), | |
135 | State: state, | |
136 | }, | |
137 | ||
138 | // Attach the state | |
139 | &AttachStateTransformer{State: state}, | |
140 | ||
141 | // Targeting | |
142 | &TargetsTransformer{Targets: n.Targets}, | |
143 | ||
144 | // Connect references so ordering is correct | |
145 | &ReferenceTransformer{}, | |
146 | ||
147 | // Make sure there is a single root | |
148 | &RootTransformer{}, | |
149 | } | |
150 | ||
151 | // Build the graph | |
152 | b := &BasicGraphBuilder{ | |
153 | Steps: steps, | |
154 | Validate: true, | |
155 | Name: "NodePlannableResource", | |
156 | } | |
157 | graph, diags := b.Build(ctx.Path()) | |
158 | return graph, diags.ErrWithWarnings() | |
159 | } |