]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go
Transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_data_refresh.go
1 package terraform
2
3 import (
4 "github.com/hashicorp/terraform/dag"
5 )
6
7 // NodeRefreshableDataResource represents a resource that is "plannable":
8 // it is ready to be planned in order to create a diff.
9 type NodeRefreshableDataResource struct {
10 *NodeAbstractCountResource
11 }
12
13 // GraphNodeDynamicExpandable
14 func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
15 // Grab the state which we read
16 state, lock := ctx.State()
17 lock.RLock()
18 defer lock.RUnlock()
19
20 // Expand the resource count which must be available by now from EvalTree
21 count, err := n.Config.Count()
22 if err != nil {
23 return nil, err
24 }
25
26 // The concrete resource factory we'll use
27 concreteResource := func(a *NodeAbstractResource) dag.Vertex {
28 // Add the config and state since we don't do that via transforms
29 a.Config = n.Config
30
31 return &NodeRefreshableDataResourceInstance{
32 NodeAbstractResource: a,
33 }
34 }
35
36 // We also need a destroyable resource for orphans that are a result of a
37 // scaled-in count.
38 concreteResourceDestroyable := func(a *NodeAbstractResource) dag.Vertex {
39 // Add the config since we don't do that via transforms
40 a.Config = n.Config
41
42 return &NodeDestroyableDataResource{
43 NodeAbstractResource: a,
44 }
45 }
46
47 // Start creating the steps
48 steps := []GraphTransformer{
49 // Expand the count.
50 &ResourceCountTransformer{
51 Concrete: concreteResource,
52 Count: count,
53 Addr: n.ResourceAddr(),
54 },
55
56 // Add the count orphans. As these are orphaned refresh nodes, we add them
57 // directly as NodeDestroyableDataResource.
58 &OrphanResourceCountTransformer{
59 Concrete: concreteResourceDestroyable,
60 Count: count,
61 Addr: n.ResourceAddr(),
62 State: state,
63 },
64
65 // Attach the state
66 &AttachStateTransformer{State: state},
67
68 // Targeting
69 &TargetsTransformer{ParsedTargets: n.Targets},
70
71 // Connect references so ordering is correct
72 &ReferenceTransformer{},
73
74 // Make sure there is a single root
75 &RootTransformer{},
76 }
77
78 // Build the graph
79 b := &BasicGraphBuilder{
80 Steps: steps,
81 Validate: true,
82 Name: "NodeRefreshableDataResource",
83 }
84
85 return b.Build(ctx.Path())
86 }
87
88 // NodeRefreshableDataResourceInstance represents a _single_ resource instance
89 // that is refreshable.
90 type NodeRefreshableDataResourceInstance struct {
91 *NodeAbstractResource
92 }
93
94 // GraphNodeEvalable
95 func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
96 addr := n.NodeAbstractResource.Addr
97
98 // stateId is the ID to put into the state
99 stateId := addr.stateId()
100
101 // Build the instance info. More of this will be populated during eval
102 info := &InstanceInfo{
103 Id: stateId,
104 Type: addr.Type,
105 }
106
107 // Get the state if we have it, if not we build it
108 rs := n.ResourceState
109 if rs == nil {
110 rs = &ResourceState{}
111 }
112
113 // If the config isn't empty we update the state
114 if n.Config != nil {
115 rs = &ResourceState{
116 Type: n.Config.Type,
117 Provider: n.Config.Provider,
118 Dependencies: n.StateReferences(),
119 }
120 }
121
122 // Build the resource for eval
123 resource := &Resource{
124 Name: addr.Name,
125 Type: addr.Type,
126 CountIndex: addr.Index,
127 }
128 if resource.CountIndex < 0 {
129 resource.CountIndex = 0
130 }
131
132 // Declare a bunch of variables that are used for state during
133 // evaluation. Most of this are written to by-address below.
134 var config *ResourceConfig
135 var diff *InstanceDiff
136 var provider ResourceProvider
137 var state *InstanceState
138
139 return &EvalSequence{
140 Nodes: []EvalNode{
141 // Always destroy the existing state first, since we must
142 // make sure that values from a previous read will not
143 // get interpolated if we end up needing to defer our
144 // loading until apply time.
145 &EvalWriteState{
146 Name: stateId,
147 ResourceType: rs.Type,
148 Provider: rs.Provider,
149 Dependencies: rs.Dependencies,
150 State: &state, // state is nil here
151 },
152
153 &EvalInterpolate{
154 Config: n.Config.RawConfig.Copy(),
155 Resource: resource,
156 Output: &config,
157 },
158
159 // The rest of this pass can proceed only if there are no
160 // computed values in our config.
161 // (If there are, we'll deal with this during the plan and
162 // apply phases.)
163 &EvalIf{
164 If: func(ctx EvalContext) (bool, error) {
165 if config.ComputedKeys != nil && len(config.ComputedKeys) > 0 {
166 return true, EvalEarlyExitError{}
167 }
168
169 // If the config explicitly has a depends_on for this
170 // data source, assume the intention is to prevent
171 // refreshing ahead of that dependency.
172 if len(n.Config.DependsOn) > 0 {
173 return true, EvalEarlyExitError{}
174 }
175
176 return true, nil
177 },
178
179 Then: EvalNoop{},
180 },
181
182 // The remainder of this pass is the same as running
183 // a "plan" pass immediately followed by an "apply" pass,
184 // populating the state early so it'll be available to
185 // provider configurations that need this data during
186 // refresh/plan.
187 &EvalGetProvider{
188 Name: n.ProvidedBy()[0],
189 Output: &provider,
190 },
191
192 &EvalReadDataDiff{
193 Info: info,
194 Config: &config,
195 Provider: &provider,
196 Output: &diff,
197 OutputState: &state,
198 },
199
200 &EvalReadDataApply{
201 Info: info,
202 Diff: &diff,
203 Provider: &provider,
204 Output: &state,
205 },
206
207 &EvalWriteState{
208 Name: stateId,
209 ResourceType: rs.Type,
210 Provider: rs.Provider,
211 Dependencies: rs.Dependencies,
212 State: &state,
213 },
214
215 &EvalUpdateStateHook{},
216 },
217 }
218 }