]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/resource.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / resource.go
1 package terraform
2
3 import (
4 "fmt"
5 "reflect"
6 "sort"
7 "strconv"
8 "strings"
9
10 "github.com/hashicorp/terraform/config"
11 "github.com/mitchellh/copystructure"
12 "github.com/mitchellh/reflectwalk"
13 )
14
15 // ResourceProvisionerConfig is used to pair a provisioner
16 // with its provided configuration. This allows us to use singleton
17 // instances of each ResourceProvisioner and to keep the relevant
18 // configuration instead of instantiating a new Provisioner for each
19 // resource.
20 type ResourceProvisionerConfig struct {
21 Type string
22 Provisioner ResourceProvisioner
23 Config *ResourceConfig
24 RawConfig *config.RawConfig
25 ConnInfo *config.RawConfig
26 }
27
28 // Resource encapsulates a resource, its configuration, its provider,
29 // its current state, and potentially a desired diff from the state it
30 // wants to reach.
31 type Resource struct {
32 // These are all used by the new EvalNode stuff.
33 Name string
34 Type string
35 CountIndex int
36
37 // These aren't really used anymore anywhere, but we keep them around
38 // since we haven't done a proper cleanup yet.
39 Id string
40 Info *InstanceInfo
41 Config *ResourceConfig
42 Dependencies []string
43 Diff *InstanceDiff
44 Provider ResourceProvider
45 State *InstanceState
46 Provisioners []*ResourceProvisionerConfig
47 Flags ResourceFlag
48 }
49
50 // ResourceKind specifies what kind of instance we're working with, whether
51 // its a primary instance, a tainted instance, or an orphan.
52 type ResourceFlag byte
53
54 // InstanceInfo is used to hold information about the instance and/or
55 // resource being modified.
56 type InstanceInfo struct {
57 // Id is a unique name to represent this instance. This is not related
58 // to InstanceState.ID in any way.
59 Id string
60
61 // ModulePath is the complete path of the module containing this
62 // instance.
63 ModulePath []string
64
65 // Type is the resource type of this instance
66 Type string
67
68 // uniqueExtra is an internal field that can be populated to supply
69 // extra metadata that is used to identify a unique instance in
70 // the graph walk. This will be appended to HumanID when uniqueId
71 // is called.
72 uniqueExtra string
73 }
74
75 // HumanId is a unique Id that is human-friendly and useful for UI elements.
76 func (i *InstanceInfo) HumanId() string {
77 if i == nil {
78 return "<nil>"
79 }
80
81 if len(i.ModulePath) <= 1 {
82 return i.Id
83 }
84
85 return fmt.Sprintf(
86 "module.%s.%s",
87 strings.Join(i.ModulePath[1:], "."),
88 i.Id)
89 }
90
91 func (i *InstanceInfo) uniqueId() string {
92 prefix := i.HumanId()
93 if v := i.uniqueExtra; v != "" {
94 prefix += " " + v
95 }
96
97 return prefix
98 }
99
100 // ResourceConfig holds the configuration given for a resource. This is
101 // done instead of a raw `map[string]interface{}` type so that rich
102 // methods can be added to it to make dealing with it easier.
103 type ResourceConfig struct {
104 ComputedKeys []string
105 Raw map[string]interface{}
106 Config map[string]interface{}
107
108 raw *config.RawConfig
109 }
110
111 // NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
112 func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
113 result := &ResourceConfig{raw: c}
114 result.interpolateForce()
115 return result
116 }
117
118 // DeepCopy performs a deep copy of the configuration. This makes it safe
119 // to modify any of the structures that are part of the resource config without
120 // affecting the original configuration.
121 func (c *ResourceConfig) DeepCopy() *ResourceConfig {
122 // DeepCopying a nil should return a nil to avoid panics
123 if c == nil {
124 return nil
125 }
126
127 // Copy, this will copy all the exported attributes
128 copy, err := copystructure.Config{Lock: true}.Copy(c)
129 if err != nil {
130 panic(err)
131 }
132
133 // Force the type
134 result := copy.(*ResourceConfig)
135
136 // For the raw configuration, we can just use its own copy method
137 result.raw = c.raw.Copy()
138
139 return result
140 }
141
142 // Equal checks the equality of two resource configs.
143 func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
144 // If either are nil, then they're only equal if they're both nil
145 if c == nil || c2 == nil {
146 return c == c2
147 }
148
149 // Sort the computed keys so they're deterministic
150 sort.Strings(c.ComputedKeys)
151 sort.Strings(c2.ComputedKeys)
152
153 // Two resource configs if their exported properties are equal.
154 // We don't compare "raw" because it is never used again after
155 // initialization and for all intents and purposes they are equal
156 // if the exported properties are equal.
157 check := [][2]interface{}{
158 {c.ComputedKeys, c2.ComputedKeys},
159 {c.Raw, c2.Raw},
160 {c.Config, c2.Config},
161 }
162 for _, pair := range check {
163 if !reflect.DeepEqual(pair[0], pair[1]) {
164 return false
165 }
166 }
167
168 return true
169 }
170
171 // CheckSet checks that the given list of configuration keys is
172 // properly set. If not, errors are returned for each unset key.
173 //
174 // This is useful to be called in the Validate method of a ResourceProvider.
175 func (c *ResourceConfig) CheckSet(keys []string) []error {
176 var errs []error
177
178 for _, k := range keys {
179 if !c.IsSet(k) {
180 errs = append(errs, fmt.Errorf("%s must be set", k))
181 }
182 }
183
184 return errs
185 }
186
187 // Get looks up a configuration value by key and returns the value.
188 //
189 // The second return value is true if the get was successful. Get will
190 // return the raw value if the key is computed, so you should pair this
191 // with IsComputed.
192 func (c *ResourceConfig) Get(k string) (interface{}, bool) {
193 // We aim to get a value from the configuration. If it is computed,
194 // then we return the pure raw value.
195 source := c.Config
196 if c.IsComputed(k) {
197 source = c.Raw
198 }
199
200 return c.get(k, source)
201 }
202
203 // GetRaw looks up a configuration value by key and returns the value,
204 // from the raw, uninterpolated config.
205 //
206 // The second return value is true if the get was successful. Get will
207 // not succeed if the value is being computed.
208 func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
209 return c.get(k, c.Raw)
210 }
211
212 // IsComputed returns whether the given key is computed or not.
213 func (c *ResourceConfig) IsComputed(k string) bool {
214 // The next thing we do is check the config if we get a computed
215 // value out of it.
216 v, ok := c.get(k, c.Config)
217 if !ok {
218 return false
219 }
220
221 // If value is nil, then it isn't computed
222 if v == nil {
223 return false
224 }
225
226 // Test if the value contains an unknown value
227 var w unknownCheckWalker
228 if err := reflectwalk.Walk(v, &w); err != nil {
229 panic(err)
230 }
231
232 return w.Unknown
233 }
234
235 // IsSet checks if the key in the configuration is set. A key is set if
236 // it has a value or the value is being computed (is unknown currently).
237 //
238 // This function should be used rather than checking the keys of the
239 // raw configuration itself, since a key may be omitted from the raw
240 // configuration if it is being computed.
241 func (c *ResourceConfig) IsSet(k string) bool {
242 if c == nil {
243 return false
244 }
245
246 if c.IsComputed(k) {
247 return true
248 }
249
250 if _, ok := c.Get(k); ok {
251 return true
252 }
253
254 return false
255 }
256
257 func (c *ResourceConfig) get(
258 k string, raw map[string]interface{}) (interface{}, bool) {
259 parts := strings.Split(k, ".")
260 if len(parts) == 1 && parts[0] == "" {
261 parts = nil
262 }
263
264 var current interface{} = raw
265 var previous interface{} = nil
266 for i, part := range parts {
267 if current == nil {
268 return nil, false
269 }
270
271 cv := reflect.ValueOf(current)
272 switch cv.Kind() {
273 case reflect.Map:
274 previous = current
275 v := cv.MapIndex(reflect.ValueOf(part))
276 if !v.IsValid() {
277 if i > 0 && i != (len(parts)-1) {
278 tryKey := strings.Join(parts[i:], ".")
279 v := cv.MapIndex(reflect.ValueOf(tryKey))
280 if !v.IsValid() {
281 return nil, false
282 }
283
284 return v.Interface(), true
285 }
286
287 return nil, false
288 }
289
290 current = v.Interface()
291 case reflect.Slice:
292 previous = current
293
294 if part == "#" {
295 // If any value in a list is computed, this whole thing
296 // is computed and we can't read any part of it.
297 for i := 0; i < cv.Len(); i++ {
298 if v := cv.Index(i).Interface(); v == unknownValue() {
299 return v, true
300 }
301 }
302
303 current = cv.Len()
304 } else {
305 i, err := strconv.ParseInt(part, 0, 0)
306 if err != nil {
307 return nil, false
308 }
309 if i >= int64(cv.Len()) {
310 return nil, false
311 }
312 current = cv.Index(int(i)).Interface()
313 }
314 case reflect.String:
315 // This happens when map keys contain "." and have a common
316 // prefix so were split as path components above.
317 actualKey := strings.Join(parts[i-1:], ".")
318 if prevMap, ok := previous.(map[string]interface{}); ok {
319 v, ok := prevMap[actualKey]
320 return v, ok
321 }
322
323 return nil, false
324 default:
325 panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
326 }
327 }
328
329 return current, true
330 }
331
332 // interpolateForce is a temporary thing. We want to get rid of interpolate
333 // above and likewise this, but it can only be done after the f-ast-graph
334 // refactor is complete.
335 func (c *ResourceConfig) interpolateForce() {
336 if c.raw == nil {
337 var err error
338 c.raw, err = config.NewRawConfig(make(map[string]interface{}))
339 if err != nil {
340 panic(err)
341 }
342 }
343
344 c.ComputedKeys = c.raw.UnknownKeys()
345 c.Raw = c.raw.RawMap()
346 c.Config = c.raw.Config()
347 }
348
349 // unknownCheckWalker
350 type unknownCheckWalker struct {
351 Unknown bool
352 }
353
354 func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
355 if v.Interface() == unknownValue() {
356 w.Unknown = true
357 }
358
359 return nil
360 }