]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go
vendor: github.com/hashicorp/terraform/...@v0.10.0
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_resource_refresh.go
CommitLineData
bae9f6d2
JC
1package terraform
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/config"
9b12e4fe 7 "github.com/hashicorp/terraform/dag"
bae9f6d2
JC
8)
9
9b12e4fe
JC
10// NodeRefreshableManagedResource represents a resource that is expanabled into
11// NodeRefreshableManagedResourceInstance. Resource count orphans are also added.
12type NodeRefreshableManagedResource struct {
13 *NodeAbstractCountResource
14}
15
16// GraphNodeDynamicExpandable
17func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
18 // Grab the state which we read
19 state, lock := ctx.State()
20 lock.RLock()
21 defer lock.RUnlock()
22
23 // Expand the resource count which must be available by now from EvalTree
24 count, err := n.Config.Count()
25 if err != nil {
26 return nil, err
27 }
28
29 // The concrete resource factory we'll use
30 concreteResource := func(a *NodeAbstractResource) dag.Vertex {
31 // Add the config and state since we don't do that via transforms
32 a.Config = n.Config
33
34 return &NodeRefreshableManagedResourceInstance{
35 NodeAbstractResource: a,
36 }
37 }
38
39 // Start creating the steps
40 steps := []GraphTransformer{
41 // Expand the count.
42 &ResourceCountTransformer{
43 Concrete: concreteResource,
44 Count: count,
45 Addr: n.ResourceAddr(),
46 },
47
9b12e4fe
JC
48 // Add the count orphans to make sure these resources are accounted for
49 // during a scale in.
50 &OrphanResourceCountTransformer{
51 Concrete: concreteResource,
52 Count: count,
53 Addr: n.ResourceAddr(),
54 State: state,
55 },
56
57 // Attach the state
58 &AttachStateTransformer{State: state},
59
60 // Targeting
61 &TargetsTransformer{ParsedTargets: n.Targets},
62
63 // Connect references so ordering is correct
64 &ReferenceTransformer{},
65
66 // Make sure there is a single root
67 &RootTransformer{},
68 }
69
70 // Build the graph
71 b := &BasicGraphBuilder{
72 Steps: steps,
73 Validate: true,
74 Name: "NodeRefreshableManagedResource",
75 }
76
77 return b.Build(ctx.Path())
78}
79
80// NodeRefreshableManagedResourceInstance represents a resource that is "applyable":
bae9f6d2 81// it is ready to be applied and is represented by a diff.
9b12e4fe 82type NodeRefreshableManagedResourceInstance struct {
bae9f6d2
JC
83 *NodeAbstractResource
84}
85
86// GraphNodeDestroyer
9b12e4fe 87func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *ResourceAddress {
bae9f6d2
JC
88 return n.Addr
89}
90
91// GraphNodeEvalable
9b12e4fe 92func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
bae9f6d2
JC
93 // Eval info is different depending on what kind of resource this is
94 switch mode := n.Addr.Mode; mode {
95 case config.ManagedResourceMode:
c680a8e1
RS
96 if n.ResourceState == nil {
97 return n.evalTreeManagedResourceNoState()
98 }
bae9f6d2
JC
99 return n.evalTreeManagedResource()
100
101 case config.DataResourceMode:
102 // Get the data source node. If we don't have a configuration
103 // then it is an orphan so we destroy it (remove it from the state).
104 var dn GraphNodeEvalable
105 if n.Config != nil {
106 dn = &NodeRefreshableDataResourceInstance{
107 NodeAbstractResource: n.NodeAbstractResource,
108 }
109 } else {
110 dn = &NodeDestroyableDataResource{
111 NodeAbstractResource: n.NodeAbstractResource,
112 }
113 }
114
115 return dn.EvalTree()
116 default:
117 panic(fmt.Errorf("unsupported resource mode %s", mode))
118 }
119}
120
9b12e4fe 121func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode {
bae9f6d2
JC
122 addr := n.NodeAbstractResource.Addr
123
124 // stateId is the ID to put into the state
125 stateId := addr.stateId()
126
127 // Build the instance info. More of this will be populated during eval
128 info := &InstanceInfo{
129 Id: stateId,
130 Type: addr.Type,
131 }
132
133 // Declare a bunch of variables that are used for state during
134 // evaluation. Most of this are written to by-address below.
135 var provider ResourceProvider
136 var state *InstanceState
137
138 // This happened during initial development. All known cases were
139 // fixed and tested but as a sanity check let's assert here.
140 if n.ResourceState == nil {
141 err := fmt.Errorf(
142 "No resource state attached for addr: %s\n\n"+
143 "This is a bug. Please report this to Terraform with your configuration\n"+
144 "and state attached. Please be careful to scrub any sensitive information.",
145 addr)
146 return &EvalReturnError{Error: &err}
147 }
148
149 return &EvalSequence{
150 Nodes: []EvalNode{
151 &EvalGetProvider{
152 Name: n.ProvidedBy()[0],
153 Output: &provider,
154 },
155 &EvalReadState{
156 Name: stateId,
157 Output: &state,
158 },
159 &EvalRefresh{
160 Info: info,
161 Provider: &provider,
162 State: &state,
163 Output: &state,
164 },
165 &EvalWriteState{
166 Name: stateId,
167 ResourceType: n.ResourceState.Type,
168 Provider: n.ResourceState.Provider,
169 Dependencies: n.ResourceState.Dependencies,
170 State: &state,
171 },
172 },
173 }
174}
c680a8e1
RS
175
176// evalTreeManagedResourceNoState produces an EvalSequence for refresh resource
177// nodes that don't have state attached. An example of where this functionality
178// is useful is when a resource that already exists in state is being scaled
179// out, ie: has its resource count increased. In this case, the scaled out node
180// needs to be available to other nodes (namely data sources) that may depend
181// on it for proper interpolation, or confusing "index out of range" errors can
182// occur.
183//
184// The steps in this sequence are very similar to the steps carried out in
185// plan, but nothing is done with the diff after it is created - it is dropped,
186// and its changes are not counted in the UI.
187func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState() EvalNode {
188 // Declare a bunch of variables that are used for state during
189 // evaluation. Most of this are written to by-address below.
190 var provider ResourceProvider
191 var state *InstanceState
192 var resourceConfig *ResourceConfig
193
194 addr := n.NodeAbstractResource.Addr
195 stateID := addr.stateId()
196 info := &InstanceInfo{
197 Id: stateID,
198 Type: addr.Type,
199 ModulePath: normalizeModulePath(addr.Path),
200 }
201
202 // Build the resource for eval
203 resource := &Resource{
204 Name: addr.Name,
205 Type: addr.Type,
206 CountIndex: addr.Index,
207 }
208 if resource.CountIndex < 0 {
209 resource.CountIndex = 0
210 }
211
212 // Determine the dependencies for the state.
213 stateDeps := n.StateReferences()
214
215 return &EvalSequence{
216 Nodes: []EvalNode{
217 &EvalInterpolate{
218 Config: n.Config.RawConfig.Copy(),
219 Resource: resource,
220 Output: &resourceConfig,
221 },
222 &EvalGetProvider{
223 Name: n.ProvidedBy()[0],
224 Output: &provider,
225 },
226 // Re-run validation to catch any errors we missed, e.g. type
227 // mismatches on computed values.
228 &EvalValidateResource{
229 Provider: &provider,
230 Config: &resourceConfig,
231 ResourceName: n.Config.Name,
232 ResourceType: n.Config.Type,
233 ResourceMode: n.Config.Mode,
234 IgnoreWarnings: true,
235 },
236 &EvalReadState{
237 Name: stateID,
238 Output: &state,
239 },
240 &EvalDiff{
241 Name: stateID,
242 Info: info,
243 Config: &resourceConfig,
244 Resource: n.Config,
245 Provider: &provider,
246 State: &state,
247 OutputState: &state,
248 Stub: true,
249 },
250 &EvalWriteState{
251 Name: stateID,
252 ResourceType: n.Config.Type,
253 Provider: n.Config.Provider,
254 Dependencies: stateDeps,
255 State: &state,
256 },
257 },
258 }
259}