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