]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ||
6 | "github.com/hashicorp/terraform/config" | |
7 | ) | |
8 | ||
9 | // NodePlannableResourceInstance represents a _single_ resource | |
10 | // instance that is plannable. This means this represents a single | |
11 | // count index, for example. | |
12 | type NodePlannableResourceInstance struct { | |
13 | *NodeAbstractResource | |
14 | } | |
15 | ||
16 | // GraphNodeEvalable | |
17 | func (n *NodePlannableResourceInstance) EvalTree() EvalNode { | |
18 | addr := n.NodeAbstractResource.Addr | |
19 | ||
20 | // stateId is the ID to put into the state | |
21 | stateId := addr.stateId() | |
22 | ||
23 | // Build the instance info. More of this will be populated during eval | |
24 | info := &InstanceInfo{ | |
25 | Id: stateId, | |
26 | Type: addr.Type, | |
27 | ModulePath: normalizeModulePath(addr.Path), | |
28 | } | |
29 | ||
30 | // Build the resource for eval | |
31 | resource := &Resource{ | |
32 | Name: addr.Name, | |
33 | Type: addr.Type, | |
34 | CountIndex: addr.Index, | |
35 | } | |
36 | if resource.CountIndex < 0 { | |
37 | resource.CountIndex = 0 | |
38 | } | |
39 | ||
40 | // Determine the dependencies for the state. | |
41 | stateDeps := n.StateReferences() | |
42 | ||
43 | // Eval info is different depending on what kind of resource this is | |
44 | switch n.Config.Mode { | |
45 | case config.ManagedResourceMode: | |
46 | return n.evalTreeManagedResource( | |
47 | stateId, info, resource, stateDeps, | |
48 | ) | |
49 | case config.DataResourceMode: | |
50 | return n.evalTreeDataResource( | |
51 | stateId, info, resource, stateDeps) | |
52 | default: | |
53 | panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) | |
54 | } | |
55 | } | |
56 | ||
57 | func (n *NodePlannableResourceInstance) evalTreeDataResource( | |
58 | stateId string, info *InstanceInfo, | |
59 | resource *Resource, stateDeps []string) EvalNode { | |
60 | var provider ResourceProvider | |
61 | var config *ResourceConfig | |
62 | var diff *InstanceDiff | |
63 | var state *InstanceState | |
64 | ||
65 | return &EvalSequence{ | |
66 | Nodes: []EvalNode{ | |
67 | &EvalReadState{ | |
68 | Name: stateId, | |
69 | Output: &state, | |
70 | }, | |
71 | ||
72 | // We need to re-interpolate the config here because some | |
73 | // of the attributes may have become computed during | |
74 | // earlier planning, due to other resources having | |
75 | // "requires new resource" diffs. | |
76 | &EvalInterpolate{ | |
77 | Config: n.Config.RawConfig.Copy(), | |
78 | Resource: resource, | |
79 | Output: &config, | |
80 | }, | |
81 | ||
82 | &EvalIf{ | |
83 | If: func(ctx EvalContext) (bool, error) { | |
84 | computed := config.ComputedKeys != nil && len(config.ComputedKeys) > 0 | |
85 | ||
86 | // If the configuration is complete and we | |
87 | // already have a state then we don't need to | |
88 | // do any further work during apply, because we | |
89 | // already populated the state during refresh. | |
90 | if !computed && state != nil { | |
91 | return true, EvalEarlyExitError{} | |
92 | } | |
93 | ||
94 | return true, nil | |
95 | }, | |
96 | Then: EvalNoop{}, | |
97 | }, | |
98 | ||
99 | &EvalGetProvider{ | |
100 | Name: n.ProvidedBy()[0], | |
101 | Output: &provider, | |
102 | }, | |
103 | ||
104 | &EvalReadDataDiff{ | |
105 | Info: info, | |
106 | Config: &config, | |
107 | Provider: &provider, | |
108 | Output: &diff, | |
109 | OutputState: &state, | |
110 | }, | |
111 | ||
112 | &EvalWriteState{ | |
113 | Name: stateId, | |
114 | ResourceType: n.Config.Type, | |
115 | Provider: n.Config.Provider, | |
116 | Dependencies: stateDeps, | |
117 | State: &state, | |
118 | }, | |
119 | ||
120 | &EvalWriteDiff{ | |
121 | Name: stateId, | |
122 | Diff: &diff, | |
123 | }, | |
124 | }, | |
125 | } | |
126 | } | |
127 | ||
128 | func (n *NodePlannableResourceInstance) evalTreeManagedResource( | |
129 | stateId string, info *InstanceInfo, | |
130 | resource *Resource, stateDeps []string) EvalNode { | |
131 | // Declare a bunch of variables that are used for state during | |
132 | // evaluation. Most of this are written to by-address below. | |
133 | var provider ResourceProvider | |
134 | var diff *InstanceDiff | |
135 | var state *InstanceState | |
136 | var resourceConfig *ResourceConfig | |
137 | ||
138 | return &EvalSequence{ | |
139 | Nodes: []EvalNode{ | |
140 | &EvalInterpolate{ | |
141 | Config: n.Config.RawConfig.Copy(), | |
142 | Resource: resource, | |
143 | Output: &resourceConfig, | |
144 | }, | |
145 | &EvalGetProvider{ | |
146 | Name: n.ProvidedBy()[0], | |
147 | Output: &provider, | |
148 | }, | |
149 | // Re-run validation to catch any errors we missed, e.g. type | |
150 | // mismatches on computed values. | |
151 | &EvalValidateResource{ | |
152 | Provider: &provider, | |
153 | Config: &resourceConfig, | |
154 | ResourceName: n.Config.Name, | |
155 | ResourceType: n.Config.Type, | |
156 | ResourceMode: n.Config.Mode, | |
157 | IgnoreWarnings: true, | |
158 | }, | |
159 | &EvalReadState{ | |
160 | Name: stateId, | |
161 | Output: &state, | |
162 | }, | |
163 | &EvalDiff{ | |
164 | Name: stateId, | |
165 | Info: info, | |
166 | Config: &resourceConfig, | |
167 | Resource: n.Config, | |
168 | Provider: &provider, | |
169 | State: &state, | |
170 | OutputDiff: &diff, | |
171 | OutputState: &state, | |
172 | }, | |
173 | &EvalCheckPreventDestroy{ | |
174 | Resource: n.Config, | |
175 | Diff: &diff, | |
176 | }, | |
177 | &EvalWriteState{ | |
178 | Name: stateId, | |
179 | ResourceType: n.Config.Type, | |
180 | Provider: n.Config.Provider, | |
181 | Dependencies: stateDeps, | |
182 | State: &state, | |
183 | }, | |
184 | &EvalWriteDiff{ | |
185 | Name: stateId, | |
186 | Diff: &diff, | |
187 | }, | |
188 | }, | |
189 | } | |
190 | } |