]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / node_data_refresh.go
CommitLineData
bae9f6d2
JC
1package terraform
2
3import (
4 "github.com/hashicorp/terraform/dag"
107c1cdb
ND
5 "github.com/hashicorp/terraform/plans"
6 "github.com/hashicorp/terraform/providers"
7 "github.com/hashicorp/terraform/states"
8 "github.com/hashicorp/terraform/tfdiags"
9 "github.com/zclconf/go-cty/cty"
bae9f6d2
JC
10)
11
107c1cdb 12// NodeRefreshableDataResource represents a resource that is "refreshable".
bae9f6d2 13type NodeRefreshableDataResource struct {
107c1cdb 14 *NodeAbstractResource
bae9f6d2
JC
15}
16
107c1cdb
ND
17var (
18 _ GraphNodeSubPath = (*NodeRefreshableDataResource)(nil)
19 _ GraphNodeDynamicExpandable = (*NodeRefreshableDataResource)(nil)
20 _ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil)
21 _ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil)
22 _ GraphNodeResource = (*NodeRefreshableDataResource)(nil)
23 _ GraphNodeAttachResourceConfig = (*NodeRefreshableDataResource)(nil)
24)
25
bae9f6d2
JC
26// GraphNodeDynamicExpandable
27func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
107c1cdb
ND
28 var diags tfdiags.Diagnostics
29
30 count, countKnown, countDiags := evaluateResourceCountExpressionKnown(n.Config.Count, ctx)
31 diags = diags.Append(countDiags)
32 if countDiags.HasErrors() {
33 return nil, diags.Err()
34 }
35 if !countKnown {
36 // If the count isn't known yet, we'll skip refreshing and try expansion
37 // again during the plan walk.
38 return nil, nil
bae9f6d2
JC
39 }
40
107c1cdb
ND
41 // Next we need to potentially rename an instance address in the state
42 // if we're transitioning whether "count" is set at all.
43 fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
44
45 // Our graph transformers require access to the full state, so we'll
46 // temporarily lock it while we work on this.
47 state := ctx.State().Lock()
48 defer ctx.State().Unlock()
49
bae9f6d2 50 // The concrete resource factory we'll use
107c1cdb 51 concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
bae9f6d2
JC
52 // Add the config and state since we don't do that via transforms
53 a.Config = n.Config
15c0b25d 54 a.ResolvedProvider = n.ResolvedProvider
bae9f6d2
JC
55
56 return &NodeRefreshableDataResourceInstance{
107c1cdb 57 NodeAbstractResourceInstance: a,
bae9f6d2
JC
58 }
59 }
60
9b12e4fe
JC
61 // We also need a destroyable resource for orphans that are a result of a
62 // scaled-in count.
107c1cdb
ND
63 concreteResourceDestroyable := func(a *NodeAbstractResourceInstance) dag.Vertex {
64 // Add the config and provider since we don't do that via transforms
9b12e4fe 65 a.Config = n.Config
107c1cdb 66 a.ResolvedProvider = n.ResolvedProvider
9b12e4fe 67
107c1cdb
ND
68 return &NodeDestroyableDataResourceInstance{
69 NodeAbstractResourceInstance: a,
9b12e4fe
JC
70 }
71 }
72
bae9f6d2
JC
73 // Start creating the steps
74 steps := []GraphTransformer{
75 // Expand the count.
76 &ResourceCountTransformer{
77 Concrete: concreteResource,
107c1cdb 78 Schema: n.Schema,
bae9f6d2
JC
79 Count: count,
80 Addr: n.ResourceAddr(),
81 },
82
9b12e4fe
JC
83 // Add the count orphans. As these are orphaned refresh nodes, we add them
84 // directly as NodeDestroyableDataResource.
85 &OrphanResourceCountTransformer{
86 Concrete: concreteResourceDestroyable,
87 Count: count,
88 Addr: n.ResourceAddr(),
89 State: state,
90 },
91
bae9f6d2
JC
92 // Attach the state
93 &AttachStateTransformer{State: state},
94
95 // Targeting
107c1cdb 96 &TargetsTransformer{Targets: n.Targets},
bae9f6d2
JC
97
98 // Connect references so ordering is correct
99 &ReferenceTransformer{},
100
101 // Make sure there is a single root
102 &RootTransformer{},
103 }
104
105 // Build the graph
106 b := &BasicGraphBuilder{
107 Steps: steps,
108 Validate: true,
109 Name: "NodeRefreshableDataResource",
110 }
111
107c1cdb
ND
112 graph, diags := b.Build(ctx.Path())
113 return graph, diags.ErrWithWarnings()
bae9f6d2
JC
114}
115
107c1cdb 116// NodeRefreshableDataResourceInstance represents a single resource instance
bae9f6d2
JC
117// that is refreshable.
118type NodeRefreshableDataResourceInstance struct {
107c1cdb 119 *NodeAbstractResourceInstance
bae9f6d2
JC
120}
121
122// GraphNodeEvalable
123func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
107c1cdb 124 addr := n.ResourceInstanceAddr()
bae9f6d2 125
107c1cdb
ND
126 // These variables are the state for the eval sequence below, and are
127 // updated through pointers.
128 var provider providers.Interface
129 var providerSchema *ProviderSchema
130 var change *plans.ResourceInstanceChange
131 var state *states.ResourceInstanceObject
132 var configVal cty.Value
bae9f6d2
JC
133
134 return &EvalSequence{
135 Nodes: []EvalNode{
107c1cdb
ND
136 &EvalGetProvider{
137 Addr: n.ResolvedProvider,
138 Output: &provider,
139 Schema: &providerSchema,
140 },
141
bae9f6d2
JC
142 // Always destroy the existing state first, since we must
143 // make sure that values from a previous read will not
144 // get interpolated if we end up needing to defer our
145 // loading until apply time.
146 &EvalWriteState{
107c1cdb
ND
147 Addr: addr.Resource,
148 ProviderAddr: n.ResolvedProvider,
149 State: &state, // a pointer to nil, here
150 ProviderSchema: &providerSchema,
bae9f6d2
JC
151 },
152
bae9f6d2
JC
153 &EvalIf{
154 If: func(ctx EvalContext) (bool, error) {
bae9f6d2
JC
155 // If the config explicitly has a depends_on for this
156 // data source, assume the intention is to prevent
107c1cdb
ND
157 // refreshing ahead of that dependency, and therefore
158 // we need to deal with this resource during the apply
159 // phase..
bae9f6d2
JC
160 if len(n.Config.DependsOn) > 0 {
161 return true, EvalEarlyExitError{}
162 }
163
164 return true, nil
165 },
bae9f6d2
JC
166 Then: EvalNoop{},
167 },
168
107c1cdb
ND
169 // EvalReadData will _attempt_ to read the data source, but may
170 // generate an incomplete planned object if the configuration
171 // includes values that won't be known until apply.
172 &EvalReadData{
173 Addr: addr.Resource,
174 Config: n.Config,
175 Dependencies: n.StateReferences(),
176 Provider: &provider,
177 ProviderAddr: n.ResolvedProvider,
178 ProviderSchema: &providerSchema,
179 OutputChange: &change,
180 OutputConfigValue: &configVal,
181 OutputState: &state,
bae9f6d2
JC
182 },
183
107c1cdb
ND
184 &EvalIf{
185 If: func(ctx EvalContext) (bool, error) {
186 return (*state).Status != states.ObjectPlanned, nil
187 },
188 Then: &EvalSequence{
189 Nodes: []EvalNode{
190 &EvalWriteState{
191 Addr: addr.Resource,
192 ProviderAddr: n.ResolvedProvider,
193 State: &state,
194 ProviderSchema: &providerSchema,
195 },
196 &EvalUpdateStateHook{},
197 },
198 },
199 Else: &EvalSequence{
200 // We can't deal with this yet, so we'll repeat this step
201 // during the plan walk to produce a planned change to read
202 // this during the apply walk. However, we do still need to
203 // save the generated change and partial state so that
204 // results from it can be included in other data resources
205 // or provider configurations during the refresh walk.
206 // (The planned object we save in the state here will be
207 // pruned out at the end of the refresh walk, returning
208 // it back to being unset again for subsequent walks.)
209 Nodes: []EvalNode{
210 &EvalWriteDiff{
211 Addr: addr.Resource,
212 Change: &change,
213 ProviderSchema: &providerSchema,
214 },
215 &EvalWriteState{
216 Addr: addr.Resource,
217 ProviderAddr: n.ResolvedProvider,
218 State: &state,
219 ProviderSchema: &providerSchema,
220 },
221 },
222 },
bae9f6d2 223 },
bae9f6d2
JC
224 },
225 }
226}