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