]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "bufio" | |
5 | "bytes" | |
6 | "encoding/json" | |
7 | "errors" | |
8 | "fmt" | |
9 | "io" | |
10 | "io/ioutil" | |
11 | "log" | |
15c0b25d | 12 | "os" |
bae9f6d2 JC |
13 | "reflect" |
14 | "sort" | |
15 | "strconv" | |
16 | "strings" | |
17 | "sync" | |
18 | ||
107c1cdb ND |
19 | "github.com/hashicorp/errwrap" |
20 | multierror "github.com/hashicorp/go-multierror" | |
21 | uuid "github.com/hashicorp/go-uuid" | |
22 | version "github.com/hashicorp/go-version" | |
23 | "github.com/hashicorp/hcl2/hcl" | |
24 | "github.com/hashicorp/hcl2/hcl/hclsyntax" | |
bae9f6d2 | 25 | "github.com/mitchellh/copystructure" |
107c1cdb ND |
26 | "github.com/zclconf/go-cty/cty" |
27 | ctyjson "github.com/zclconf/go-cty/cty/json" | |
15c0b25d | 28 | |
107c1cdb ND |
29 | "github.com/hashicorp/terraform/addrs" |
30 | "github.com/hashicorp/terraform/config" | |
31 | "github.com/hashicorp/terraform/config/hcl2shim" | |
32 | "github.com/hashicorp/terraform/configs" | |
33 | "github.com/hashicorp/terraform/configs/configschema" | |
34 | "github.com/hashicorp/terraform/plans" | |
35 | "github.com/hashicorp/terraform/tfdiags" | |
15c0b25d | 36 | tfversion "github.com/hashicorp/terraform/version" |
bae9f6d2 JC |
37 | ) |
38 | ||
39 | const ( | |
40 | // StateVersion is the current version for our state file | |
41 | StateVersion = 3 | |
42 | ) | |
43 | ||
44 | // rootModulePath is the path of the root module | |
45 | var rootModulePath = []string{"root"} | |
46 | ||
107c1cdb ND |
47 | // normalizeModulePath transforms a legacy module path (which may or may not |
48 | // have a redundant "root" label at the start of it) into an | |
49 | // addrs.ModuleInstance representing the same module. | |
50 | // | |
51 | // For legacy reasons, different parts of Terraform disagree about whether the | |
52 | // root module has the path []string{} or []string{"root"}, and so this | |
53 | // function accepts both and trims off the "root". An implication of this is | |
54 | // that it's not possible to actually have a module call in the root module | |
55 | // that is itself named "root", since that would be ambiguous. | |
56 | // | |
bae9f6d2 JC |
57 | // normalizeModulePath takes a raw module path and returns a path that |
58 | // has the rootModulePath prepended to it. If I could go back in time I | |
59 | // would've never had a rootModulePath (empty path would be root). We can | |
60 | // still fix this but thats a big refactor that my branch doesn't make sense | |
61 | // for. Instead, this function normalizes paths. | |
107c1cdb ND |
62 | func normalizeModulePath(p []string) addrs.ModuleInstance { |
63 | // FIXME: Remove this once everyone is using addrs.ModuleInstance. | |
bae9f6d2 | 64 | |
107c1cdb ND |
65 | if len(p) > 0 && p[0] == "root" { |
66 | p = p[1:] | |
bae9f6d2 JC |
67 | } |
68 | ||
107c1cdb ND |
69 | ret := make(addrs.ModuleInstance, len(p)) |
70 | for i, name := range p { | |
71 | // For now we don't actually support modules with multiple instances | |
72 | // identified by keys, so we just treat every path element as a | |
73 | // step with no key. | |
74 | ret[i] = addrs.ModuleInstanceStep{ | |
75 | Name: name, | |
76 | } | |
77 | } | |
78 | return ret | |
bae9f6d2 JC |
79 | } |
80 | ||
81 | // State keeps track of a snapshot state-of-the-world that Terraform | |
82 | // can use to keep track of what real world resources it is actually | |
83 | // managing. | |
84 | type State struct { | |
85 | // Version is the state file protocol version. | |
86 | Version int `json:"version"` | |
87 | ||
88 | // TFVersion is the version of Terraform that wrote this state. | |
89 | TFVersion string `json:"terraform_version,omitempty"` | |
90 | ||
91 | // Serial is incremented on any operation that modifies | |
92 | // the State file. It is used to detect potentially conflicting | |
93 | // updates. | |
94 | Serial int64 `json:"serial"` | |
95 | ||
96 | // Lineage is set when a new, blank state is created and then | |
97 | // never updated. This allows us to determine whether the serials | |
98 | // of two states can be meaningfully compared. | |
99 | // Apart from the guarantee that collisions between two lineages | |
100 | // are very unlikely, this value is opaque and external callers | |
101 | // should only compare lineage strings byte-for-byte for equality. | |
102 | Lineage string `json:"lineage"` | |
103 | ||
104 | // Remote is used to track the metadata required to | |
105 | // pull and push state files from a remote storage endpoint. | |
106 | Remote *RemoteState `json:"remote,omitempty"` | |
107 | ||
108 | // Backend tracks the configuration for the backend in use with | |
109 | // this state. This is used to track any changes in the backend | |
110 | // configuration. | |
111 | Backend *BackendState `json:"backend,omitempty"` | |
112 | ||
113 | // Modules contains all the modules in a breadth-first order | |
114 | Modules []*ModuleState `json:"modules"` | |
115 | ||
116 | mu sync.Mutex | |
117 | } | |
118 | ||
119 | func (s *State) Lock() { s.mu.Lock() } | |
120 | func (s *State) Unlock() { s.mu.Unlock() } | |
121 | ||
122 | // NewState is used to initialize a blank state | |
123 | func NewState() *State { | |
124 | s := &State{} | |
125 | s.init() | |
126 | return s | |
127 | } | |
128 | ||
129 | // Children returns the ModuleStates that are direct children of | |
130 | // the given path. If the path is "root", for example, then children | |
131 | // returned might be "root.child", but not "root.child.grandchild". | |
132 | func (s *State) Children(path []string) []*ModuleState { | |
133 | s.Lock() | |
134 | defer s.Unlock() | |
135 | // TODO: test | |
136 | ||
137 | return s.children(path) | |
138 | } | |
139 | ||
140 | func (s *State) children(path []string) []*ModuleState { | |
141 | result := make([]*ModuleState, 0) | |
142 | for _, m := range s.Modules { | |
143 | if m == nil { | |
144 | continue | |
145 | } | |
146 | ||
147 | if len(m.Path) != len(path)+1 { | |
148 | continue | |
149 | } | |
150 | if !reflect.DeepEqual(path, m.Path[:len(path)]) { | |
151 | continue | |
152 | } | |
153 | ||
154 | result = append(result, m) | |
155 | } | |
156 | ||
157 | return result | |
158 | } | |
159 | ||
160 | // AddModule adds the module with the given path to the state. | |
161 | // | |
162 | // This should be the preferred method to add module states since it | |
163 | // allows us to optimize lookups later as well as control sorting. | |
107c1cdb | 164 | func (s *State) AddModule(path addrs.ModuleInstance) *ModuleState { |
bae9f6d2 JC |
165 | s.Lock() |
166 | defer s.Unlock() | |
167 | ||
168 | return s.addModule(path) | |
169 | } | |
170 | ||
107c1cdb | 171 | func (s *State) addModule(path addrs.ModuleInstance) *ModuleState { |
bae9f6d2 JC |
172 | // check if the module exists first |
173 | m := s.moduleByPath(path) | |
174 | if m != nil { | |
175 | return m | |
176 | } | |
177 | ||
107c1cdb ND |
178 | // Lower the new-style address into a legacy-style address. |
179 | // This requires that none of the steps have instance keys, which is | |
180 | // true for all addresses at the time of implementing this because | |
181 | // "count" and "for_each" are not yet implemented for modules. | |
182 | // For the purposes of state, the legacy address format also includes | |
183 | // a redundant extra prefix element "root". It is important to include | |
184 | // this because the "prune" method will remove any module that has a | |
185 | // path length less than one, and other parts of the state code will | |
186 | // trim off the first element indiscriminately. | |
187 | legacyPath := make([]string, len(path)+1) | |
188 | legacyPath[0] = "root" | |
189 | for i, step := range path { | |
190 | if step.InstanceKey != addrs.NoKey { | |
191 | // FIXME: Once the rest of Terraform is ready to use count and | |
192 | // for_each, remove all of this and just write the addrs.ModuleInstance | |
193 | // value itself into the ModuleState. | |
194 | panic("state cannot represent modules with count or for_each keys") | |
195 | } | |
196 | ||
197 | legacyPath[i+1] = step.Name | |
198 | } | |
199 | ||
200 | m = &ModuleState{Path: legacyPath} | |
bae9f6d2 JC |
201 | m.init() |
202 | s.Modules = append(s.Modules, m) | |
203 | s.sort() | |
204 | return m | |
205 | } | |
206 | ||
207 | // ModuleByPath is used to lookup the module state for the given path. | |
208 | // This should be the preferred lookup mechanism as it allows for future | |
209 | // lookup optimizations. | |
107c1cdb | 210 | func (s *State) ModuleByPath(path addrs.ModuleInstance) *ModuleState { |
bae9f6d2 JC |
211 | if s == nil { |
212 | return nil | |
213 | } | |
214 | s.Lock() | |
215 | defer s.Unlock() | |
216 | ||
217 | return s.moduleByPath(path) | |
218 | } | |
219 | ||
107c1cdb | 220 | func (s *State) moduleByPath(path addrs.ModuleInstance) *ModuleState { |
bae9f6d2 JC |
221 | for _, mod := range s.Modules { |
222 | if mod == nil { | |
223 | continue | |
224 | } | |
225 | if mod.Path == nil { | |
226 | panic("missing module path") | |
227 | } | |
107c1cdb ND |
228 | modPath := normalizeModulePath(mod.Path) |
229 | if modPath.String() == path.String() { | |
bae9f6d2 JC |
230 | return mod |
231 | } | |
232 | } | |
233 | return nil | |
234 | } | |
235 | ||
bae9f6d2 JC |
236 | // Empty returns true if the state is empty. |
237 | func (s *State) Empty() bool { | |
238 | if s == nil { | |
239 | return true | |
240 | } | |
241 | s.Lock() | |
242 | defer s.Unlock() | |
243 | ||
244 | return len(s.Modules) == 0 | |
245 | } | |
246 | ||
247 | // HasResources returns true if the state contains any resources. | |
248 | // | |
249 | // This is similar to !s.Empty, but returns true also in the case where the | |
250 | // state has modules but all of them are devoid of resources. | |
251 | func (s *State) HasResources() bool { | |
252 | if s.Empty() { | |
253 | return false | |
254 | } | |
255 | ||
256 | for _, mod := range s.Modules { | |
257 | if len(mod.Resources) > 0 { | |
258 | return true | |
259 | } | |
260 | } | |
261 | ||
262 | return false | |
263 | } | |
264 | ||
265 | // IsRemote returns true if State represents a state that exists and is | |
266 | // remote. | |
267 | func (s *State) IsRemote() bool { | |
268 | if s == nil { | |
269 | return false | |
270 | } | |
271 | s.Lock() | |
272 | defer s.Unlock() | |
273 | ||
274 | if s.Remote == nil { | |
275 | return false | |
276 | } | |
277 | if s.Remote.Type == "" { | |
278 | return false | |
279 | } | |
280 | ||
281 | return true | |
282 | } | |
283 | ||
284 | // Validate validates the integrity of this state file. | |
285 | // | |
286 | // Certain properties of the statefile are expected by Terraform in order | |
287 | // to behave properly. The core of Terraform will assume that once it | |
288 | // receives a State structure that it has been validated. This validation | |
289 | // check should be called to ensure that. | |
290 | // | |
291 | // If this returns an error, then the user should be notified. The error | |
292 | // response will include detailed information on the nature of the error. | |
293 | func (s *State) Validate() error { | |
294 | s.Lock() | |
295 | defer s.Unlock() | |
296 | ||
297 | var result error | |
298 | ||
299 | // !!!! FOR DEVELOPERS !!!! | |
300 | // | |
301 | // Any errors returned from this Validate function will BLOCK TERRAFORM | |
302 | // from loading a state file. Therefore, this should only contain checks | |
303 | // that are only resolvable through manual intervention. | |
304 | // | |
305 | // !!!! FOR DEVELOPERS !!!! | |
306 | ||
307 | // Make sure there are no duplicate module states. We open a new | |
308 | // block here so we can use basic variable names and future validations | |
309 | // can do the same. | |
310 | { | |
311 | found := make(map[string]struct{}) | |
312 | for _, ms := range s.Modules { | |
313 | if ms == nil { | |
314 | continue | |
315 | } | |
316 | ||
317 | key := strings.Join(ms.Path, ".") | |
318 | if _, ok := found[key]; ok { | |
319 | result = multierror.Append(result, fmt.Errorf( | |
320 | strings.TrimSpace(stateValidateErrMultiModule), key)) | |
321 | continue | |
322 | } | |
323 | ||
324 | found[key] = struct{}{} | |
325 | } | |
326 | } | |
327 | ||
328 | return result | |
329 | } | |
330 | ||
331 | // Remove removes the item in the state at the given address, returning | |
332 | // any errors that may have occurred. | |
333 | // | |
334 | // If the address references a module state or resource, it will delete | |
335 | // all children as well. To check what will be deleted, use a StateFilter | |
336 | // first. | |
337 | func (s *State) Remove(addr ...string) error { | |
338 | s.Lock() | |
339 | defer s.Unlock() | |
340 | ||
341 | // Filter out what we need to delete | |
342 | filter := &StateFilter{State: s} | |
343 | results, err := filter.Filter(addr...) | |
344 | if err != nil { | |
345 | return err | |
346 | } | |
347 | ||
348 | // If we have no results, just exit early, we're not going to do anything. | |
349 | // While what happens below is fairly fast, this is an important early | |
350 | // exit since the prune below might modify the state more and we don't | |
351 | // want to modify the state if we don't have to. | |
352 | if len(results) == 0 { | |
353 | return nil | |
354 | } | |
355 | ||
356 | // Go through each result and grab what we need | |
357 | removed := make(map[interface{}]struct{}) | |
358 | for _, r := range results { | |
359 | // Convert the path to our own type | |
360 | path := append([]string{"root"}, r.Path...) | |
361 | ||
362 | // If we removed this already, then ignore | |
363 | if _, ok := removed[r.Value]; ok { | |
364 | continue | |
365 | } | |
366 | ||
367 | // If we removed the parent already, then ignore | |
368 | if r.Parent != nil { | |
369 | if _, ok := removed[r.Parent.Value]; ok { | |
370 | continue | |
371 | } | |
372 | } | |
373 | ||
374 | // Add this to the removed list | |
375 | removed[r.Value] = struct{}{} | |
376 | ||
377 | switch v := r.Value.(type) { | |
378 | case *ModuleState: | |
379 | s.removeModule(path, v) | |
380 | case *ResourceState: | |
381 | s.removeResource(path, v) | |
382 | case *InstanceState: | |
383 | s.removeInstance(path, r.Parent.Value.(*ResourceState), v) | |
384 | default: | |
385 | return fmt.Errorf("unknown type to delete: %T", r.Value) | |
386 | } | |
387 | } | |
388 | ||
389 | // Prune since the removal functions often do the bare minimum to | |
390 | // remove a thing and may leave around dangling empty modules, resources, | |
391 | // etc. Prune will clean that all up. | |
392 | s.prune() | |
393 | ||
394 | return nil | |
395 | } | |
396 | ||
397 | func (s *State) removeModule(path []string, v *ModuleState) { | |
398 | for i, m := range s.Modules { | |
399 | if m == v { | |
400 | s.Modules, s.Modules[len(s.Modules)-1] = append(s.Modules[:i], s.Modules[i+1:]...), nil | |
401 | return | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | func (s *State) removeResource(path []string, v *ResourceState) { | |
407 | // Get the module this resource lives in. If it doesn't exist, we're done. | |
107c1cdb | 408 | mod := s.moduleByPath(normalizeModulePath(path)) |
bae9f6d2 JC |
409 | if mod == nil { |
410 | return | |
411 | } | |
412 | ||
413 | // Find this resource. This is a O(N) lookup when if we had the key | |
414 | // it could be O(1) but even with thousands of resources this shouldn't | |
415 | // matter right now. We can easily up performance here when the time comes. | |
416 | for k, r := range mod.Resources { | |
417 | if r == v { | |
418 | // Found it | |
419 | delete(mod.Resources, k) | |
420 | return | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
425 | func (s *State) removeInstance(path []string, r *ResourceState, v *InstanceState) { | |
426 | // Go through the resource and find the instance that matches this | |
427 | // (if any) and remove it. | |
428 | ||
429 | // Check primary | |
430 | if r.Primary == v { | |
431 | r.Primary = nil | |
432 | return | |
433 | } | |
434 | ||
435 | // Check lists | |
436 | lists := [][]*InstanceState{r.Deposed} | |
437 | for _, is := range lists { | |
438 | for i, instance := range is { | |
439 | if instance == v { | |
440 | // Found it, remove it | |
441 | is, is[len(is)-1] = append(is[:i], is[i+1:]...), nil | |
442 | ||
443 | // Done | |
444 | return | |
445 | } | |
446 | } | |
447 | } | |
448 | } | |
449 | ||
450 | // RootModule returns the ModuleState for the root module | |
451 | func (s *State) RootModule() *ModuleState { | |
107c1cdb | 452 | root := s.ModuleByPath(addrs.RootModuleInstance) |
bae9f6d2 JC |
453 | if root == nil { |
454 | panic("missing root module") | |
455 | } | |
456 | return root | |
457 | } | |
458 | ||
459 | // Equal tests if one state is equal to another. | |
460 | func (s *State) Equal(other *State) bool { | |
461 | // If one is nil, we do a direct check | |
462 | if s == nil || other == nil { | |
463 | return s == other | |
464 | } | |
465 | ||
466 | s.Lock() | |
467 | defer s.Unlock() | |
468 | return s.equal(other) | |
469 | } | |
470 | ||
471 | func (s *State) equal(other *State) bool { | |
472 | if s == nil || other == nil { | |
473 | return s == other | |
474 | } | |
475 | ||
476 | // If the versions are different, they're certainly not equal | |
477 | if s.Version != other.Version { | |
478 | return false | |
479 | } | |
480 | ||
481 | // If any of the modules are not equal, then this state isn't equal | |
482 | if len(s.Modules) != len(other.Modules) { | |
483 | return false | |
484 | } | |
485 | for _, m := range s.Modules { | |
486 | // This isn't very optimal currently but works. | |
107c1cdb | 487 | otherM := other.moduleByPath(normalizeModulePath(m.Path)) |
bae9f6d2 JC |
488 | if otherM == nil { |
489 | return false | |
490 | } | |
491 | ||
492 | // If they're not equal, then we're not equal! | |
493 | if !m.Equal(otherM) { | |
494 | return false | |
495 | } | |
496 | } | |
497 | ||
498 | return true | |
499 | } | |
500 | ||
c680a8e1 RS |
501 | // MarshalEqual is similar to Equal but provides a stronger definition of |
502 | // "equal", where two states are equal if and only if their serialized form | |
503 | // is byte-for-byte identical. | |
504 | // | |
505 | // This is primarily useful for callers that are trying to save snapshots | |
506 | // of state to persistent storage, allowing them to detect when a new | |
507 | // snapshot must be taken. | |
508 | // | |
509 | // Note that the serial number and lineage are included in the serialized form, | |
510 | // so it's the caller's responsibility to properly manage these attributes | |
511 | // so that this method is only called on two states that have the same | |
512 | // serial and lineage, unless detecting such differences is desired. | |
513 | func (s *State) MarshalEqual(other *State) bool { | |
514 | if s == nil && other == nil { | |
515 | return true | |
516 | } else if s == nil || other == nil { | |
517 | return false | |
518 | } | |
519 | ||
520 | recvBuf := &bytes.Buffer{} | |
521 | otherBuf := &bytes.Buffer{} | |
522 | ||
523 | err := WriteState(s, recvBuf) | |
524 | if err != nil { | |
525 | // should never happen, since we're writing to a buffer | |
526 | panic(err) | |
527 | } | |
528 | ||
529 | err = WriteState(other, otherBuf) | |
530 | if err != nil { | |
531 | // should never happen, since we're writing to a buffer | |
532 | panic(err) | |
533 | } | |
534 | ||
535 | return bytes.Equal(recvBuf.Bytes(), otherBuf.Bytes()) | |
536 | } | |
537 | ||
bae9f6d2 JC |
538 | type StateAgeComparison int |
539 | ||
540 | const ( | |
541 | StateAgeEqual StateAgeComparison = 0 | |
542 | StateAgeReceiverNewer StateAgeComparison = 1 | |
543 | StateAgeReceiverOlder StateAgeComparison = -1 | |
544 | ) | |
545 | ||
546 | // CompareAges compares one state with another for which is "older". | |
547 | // | |
548 | // This is a simple check using the state's serial, and is thus only as | |
549 | // reliable as the serial itself. In the normal case, only one state | |
550 | // exists for a given combination of lineage/serial, but Terraform | |
551 | // does not guarantee this and so the result of this method should be | |
552 | // used with care. | |
553 | // | |
554 | // Returns an integer that is negative if the receiver is older than | |
555 | // the argument, positive if the converse, and zero if they are equal. | |
556 | // An error is returned if the two states are not of the same lineage, | |
557 | // in which case the integer returned has no meaning. | |
558 | func (s *State) CompareAges(other *State) (StateAgeComparison, error) { | |
559 | // nil states are "older" than actual states | |
560 | switch { | |
561 | case s != nil && other == nil: | |
562 | return StateAgeReceiverNewer, nil | |
563 | case s == nil && other != nil: | |
564 | return StateAgeReceiverOlder, nil | |
565 | case s == nil && other == nil: | |
566 | return StateAgeEqual, nil | |
567 | } | |
568 | ||
569 | if !s.SameLineage(other) { | |
570 | return StateAgeEqual, fmt.Errorf( | |
571 | "can't compare two states of differing lineage", | |
572 | ) | |
573 | } | |
574 | ||
575 | s.Lock() | |
576 | defer s.Unlock() | |
577 | ||
578 | switch { | |
579 | case s.Serial < other.Serial: | |
580 | return StateAgeReceiverOlder, nil | |
581 | case s.Serial > other.Serial: | |
582 | return StateAgeReceiverNewer, nil | |
583 | default: | |
584 | return StateAgeEqual, nil | |
585 | } | |
586 | } | |
587 | ||
588 | // SameLineage returns true only if the state given in argument belongs | |
589 | // to the same "lineage" of states as the receiver. | |
590 | func (s *State) SameLineage(other *State) bool { | |
591 | s.Lock() | |
592 | defer s.Unlock() | |
593 | ||
594 | // If one of the states has no lineage then it is assumed to predate | |
595 | // this concept, and so we'll accept it as belonging to any lineage | |
596 | // so that a lineage string can be assigned to newer versions | |
597 | // without breaking compatibility with older versions. | |
598 | if s.Lineage == "" || other.Lineage == "" { | |
599 | return true | |
600 | } | |
601 | ||
602 | return s.Lineage == other.Lineage | |
603 | } | |
604 | ||
605 | // DeepCopy performs a deep copy of the state structure and returns | |
606 | // a new structure. | |
607 | func (s *State) DeepCopy() *State { | |
c680a8e1 RS |
608 | if s == nil { |
609 | return nil | |
610 | } | |
611 | ||
bae9f6d2 JC |
612 | copy, err := copystructure.Config{Lock: true}.Copy(s) |
613 | if err != nil { | |
614 | panic(err) | |
615 | } | |
616 | ||
617 | return copy.(*State) | |
618 | } | |
619 | ||
bae9f6d2 JC |
620 | // FromFutureTerraform checks if this state was written by a Terraform |
621 | // version from the future. | |
622 | func (s *State) FromFutureTerraform() bool { | |
623 | s.Lock() | |
624 | defer s.Unlock() | |
625 | ||
626 | // No TF version means it is certainly from the past | |
627 | if s.TFVersion == "" { | |
628 | return false | |
629 | } | |
630 | ||
631 | v := version.Must(version.NewVersion(s.TFVersion)) | |
15c0b25d | 632 | return tfversion.SemVer.LessThan(v) |
bae9f6d2 JC |
633 | } |
634 | ||
635 | func (s *State) Init() { | |
636 | s.Lock() | |
637 | defer s.Unlock() | |
638 | s.init() | |
639 | } | |
640 | ||
641 | func (s *State) init() { | |
642 | if s.Version == 0 { | |
643 | s.Version = StateVersion | |
644 | } | |
c680a8e1 | 645 | |
107c1cdb ND |
646 | if s.moduleByPath(addrs.RootModuleInstance) == nil { |
647 | s.addModule(addrs.RootModuleInstance) | |
bae9f6d2 JC |
648 | } |
649 | s.ensureHasLineage() | |
650 | ||
651 | for _, mod := range s.Modules { | |
652 | if mod != nil { | |
653 | mod.init() | |
654 | } | |
655 | } | |
656 | ||
657 | if s.Remote != nil { | |
658 | s.Remote.init() | |
659 | } | |
660 | ||
661 | } | |
662 | ||
663 | func (s *State) EnsureHasLineage() { | |
664 | s.Lock() | |
665 | defer s.Unlock() | |
666 | ||
667 | s.ensureHasLineage() | |
668 | } | |
669 | ||
670 | func (s *State) ensureHasLineage() { | |
671 | if s.Lineage == "" { | |
15c0b25d AP |
672 | lineage, err := uuid.GenerateUUID() |
673 | if err != nil { | |
674 | panic(fmt.Errorf("Failed to generate lineage: %v", err)) | |
675 | } | |
676 | s.Lineage = lineage | |
bae9f6d2 JC |
677 | log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) |
678 | } else { | |
679 | log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage) | |
680 | } | |
681 | } | |
682 | ||
683 | // AddModuleState insert this module state and override any existing ModuleState | |
684 | func (s *State) AddModuleState(mod *ModuleState) { | |
685 | mod.init() | |
686 | s.Lock() | |
687 | defer s.Unlock() | |
688 | ||
689 | s.addModuleState(mod) | |
690 | } | |
691 | ||
692 | func (s *State) addModuleState(mod *ModuleState) { | |
693 | for i, m := range s.Modules { | |
694 | if reflect.DeepEqual(m.Path, mod.Path) { | |
695 | s.Modules[i] = mod | |
696 | return | |
697 | } | |
698 | } | |
699 | ||
700 | s.Modules = append(s.Modules, mod) | |
701 | s.sort() | |
702 | } | |
703 | ||
704 | // prune is used to remove any resources that are no longer required | |
705 | func (s *State) prune() { | |
706 | if s == nil { | |
707 | return | |
708 | } | |
709 | ||
710 | // Filter out empty modules. | |
711 | // A module is always assumed to have a path, and it's length isn't always | |
712 | // bounds checked later on. Modules may be "emptied" during destroy, but we | |
713 | // never want to store those in the state. | |
714 | for i := 0; i < len(s.Modules); i++ { | |
715 | if s.Modules[i] == nil || len(s.Modules[i].Path) == 0 { | |
716 | s.Modules = append(s.Modules[:i], s.Modules[i+1:]...) | |
717 | i-- | |
718 | } | |
719 | } | |
720 | ||
721 | for _, mod := range s.Modules { | |
722 | mod.prune() | |
723 | } | |
724 | if s.Remote != nil && s.Remote.Empty() { | |
725 | s.Remote = nil | |
726 | } | |
727 | } | |
728 | ||
729 | // sort sorts the modules | |
730 | func (s *State) sort() { | |
731 | sort.Sort(moduleStateSort(s.Modules)) | |
732 | ||
733 | // Allow modules to be sorted | |
734 | for _, m := range s.Modules { | |
735 | if m != nil { | |
736 | m.sort() | |
737 | } | |
738 | } | |
739 | } | |
740 | ||
741 | func (s *State) String() string { | |
742 | if s == nil { | |
743 | return "<nil>" | |
744 | } | |
745 | s.Lock() | |
746 | defer s.Unlock() | |
747 | ||
748 | var buf bytes.Buffer | |
749 | for _, m := range s.Modules { | |
750 | mStr := m.String() | |
751 | ||
752 | // If we're the root module, we just write the output directly. | |
753 | if reflect.DeepEqual(m.Path, rootModulePath) { | |
754 | buf.WriteString(mStr + "\n") | |
755 | continue | |
756 | } | |
757 | ||
758 | buf.WriteString(fmt.Sprintf("module.%s:\n", strings.Join(m.Path[1:], "."))) | |
759 | ||
760 | s := bufio.NewScanner(strings.NewReader(mStr)) | |
761 | for s.Scan() { | |
762 | text := s.Text() | |
763 | if text != "" { | |
764 | text = " " + text | |
765 | } | |
766 | ||
767 | buf.WriteString(fmt.Sprintf("%s\n", text)) | |
768 | } | |
769 | } | |
770 | ||
771 | return strings.TrimSpace(buf.String()) | |
772 | } | |
773 | ||
774 | // BackendState stores the configuration to connect to a remote backend. | |
775 | type BackendState struct { | |
107c1cdb ND |
776 | Type string `json:"type"` // Backend type |
777 | ConfigRaw json.RawMessage `json:"config"` // Backend raw config | |
778 | Hash uint64 `json:"hash"` // Hash of portion of configuration from config files | |
bae9f6d2 JC |
779 | } |
780 | ||
781 | // Empty returns true if BackendState has no state. | |
782 | func (s *BackendState) Empty() bool { | |
783 | return s == nil || s.Type == "" | |
784 | } | |
785 | ||
107c1cdb ND |
786 | // Config decodes the type-specific configuration object using the provided |
787 | // schema and returns the result as a cty.Value. | |
788 | // | |
789 | // An error is returned if the stored configuration does not conform to the | |
790 | // given schema. | |
791 | func (s *BackendState) Config(schema *configschema.Block) (cty.Value, error) { | |
792 | ty := schema.ImpliedType() | |
bae9f6d2 | 793 | if s == nil { |
107c1cdb | 794 | return cty.NullVal(ty), nil |
bae9f6d2 | 795 | } |
107c1cdb ND |
796 | return ctyjson.Unmarshal(s.ConfigRaw, ty) |
797 | } | |
bae9f6d2 | 798 | |
107c1cdb ND |
799 | // SetConfig replaces (in-place) the type-specific configuration object using |
800 | // the provided value and associated schema. | |
801 | // | |
802 | // An error is returned if the given value does not conform to the implied | |
803 | // type of the schema. | |
804 | func (s *BackendState) SetConfig(val cty.Value, schema *configschema.Block) error { | |
805 | ty := schema.ImpliedType() | |
806 | buf, err := ctyjson.Marshal(val, ty) | |
807 | if err != nil { | |
808 | return err | |
bae9f6d2 | 809 | } |
107c1cdb ND |
810 | s.ConfigRaw = buf |
811 | return nil | |
812 | } | |
bae9f6d2 | 813 | |
107c1cdb ND |
814 | // ForPlan produces an alternative representation of the reciever that is |
815 | // suitable for storing in a plan. The current workspace must additionally | |
816 | // be provided, to be stored alongside the backend configuration. | |
817 | // | |
818 | // The backend configuration schema is required in order to properly | |
819 | // encode the backend-specific configuration settings. | |
820 | func (s *BackendState) ForPlan(schema *configschema.Block, workspaceName string) (*plans.Backend, error) { | |
821 | if s == nil { | |
822 | return nil, nil | |
823 | } | |
824 | ||
825 | configVal, err := s.Config(schema) | |
826 | if err != nil { | |
827 | return nil, errwrap.Wrapf("failed to decode backend config: {{err}}", err) | |
828 | } | |
829 | return plans.NewBackend(s.Type, configVal, schema, workspaceName) | |
bae9f6d2 JC |
830 | } |
831 | ||
832 | // RemoteState is used to track the information about a remote | |
833 | // state store that we push/pull state to. | |
834 | type RemoteState struct { | |
835 | // Type controls the client we use for the remote state | |
836 | Type string `json:"type"` | |
837 | ||
838 | // Config is used to store arbitrary configuration that | |
839 | // is type specific | |
840 | Config map[string]string `json:"config"` | |
841 | ||
842 | mu sync.Mutex | |
843 | } | |
844 | ||
845 | func (s *RemoteState) Lock() { s.mu.Lock() } | |
846 | func (s *RemoteState) Unlock() { s.mu.Unlock() } | |
847 | ||
848 | func (r *RemoteState) init() { | |
849 | r.Lock() | |
850 | defer r.Unlock() | |
851 | ||
852 | if r.Config == nil { | |
853 | r.Config = make(map[string]string) | |
854 | } | |
855 | } | |
856 | ||
857 | func (r *RemoteState) deepcopy() *RemoteState { | |
858 | r.Lock() | |
859 | defer r.Unlock() | |
860 | ||
861 | confCopy := make(map[string]string, len(r.Config)) | |
862 | for k, v := range r.Config { | |
863 | confCopy[k] = v | |
864 | } | |
865 | return &RemoteState{ | |
866 | Type: r.Type, | |
867 | Config: confCopy, | |
868 | } | |
869 | } | |
870 | ||
871 | func (r *RemoteState) Empty() bool { | |
872 | if r == nil { | |
873 | return true | |
874 | } | |
875 | r.Lock() | |
876 | defer r.Unlock() | |
877 | ||
878 | return r.Type == "" | |
879 | } | |
880 | ||
881 | func (r *RemoteState) Equals(other *RemoteState) bool { | |
882 | r.Lock() | |
883 | defer r.Unlock() | |
884 | ||
885 | if r.Type != other.Type { | |
886 | return false | |
887 | } | |
888 | if len(r.Config) != len(other.Config) { | |
889 | return false | |
890 | } | |
891 | for k, v := range r.Config { | |
892 | if other.Config[k] != v { | |
893 | return false | |
894 | } | |
895 | } | |
896 | return true | |
897 | } | |
898 | ||
899 | // OutputState is used to track the state relevant to a single output. | |
900 | type OutputState struct { | |
901 | // Sensitive describes whether the output is considered sensitive, | |
902 | // which may lead to masking the value on screen in some cases. | |
903 | Sensitive bool `json:"sensitive"` | |
904 | // Type describes the structure of Value. Valid values are "string", | |
905 | // "map" and "list" | |
906 | Type string `json:"type"` | |
907 | // Value contains the value of the output, in the structure described | |
908 | // by the Type field. | |
909 | Value interface{} `json:"value"` | |
910 | ||
911 | mu sync.Mutex | |
912 | } | |
913 | ||
914 | func (s *OutputState) Lock() { s.mu.Lock() } | |
915 | func (s *OutputState) Unlock() { s.mu.Unlock() } | |
916 | ||
917 | func (s *OutputState) String() string { | |
918 | return fmt.Sprintf("%#v", s.Value) | |
919 | } | |
920 | ||
921 | // Equal compares two OutputState structures for equality. nil values are | |
922 | // considered equal. | |
923 | func (s *OutputState) Equal(other *OutputState) bool { | |
924 | if s == nil && other == nil { | |
925 | return true | |
926 | } | |
927 | ||
928 | if s == nil || other == nil { | |
929 | return false | |
930 | } | |
931 | s.Lock() | |
932 | defer s.Unlock() | |
933 | ||
934 | if s.Type != other.Type { | |
935 | return false | |
936 | } | |
937 | ||
938 | if s.Sensitive != other.Sensitive { | |
939 | return false | |
940 | } | |
941 | ||
942 | if !reflect.DeepEqual(s.Value, other.Value) { | |
943 | return false | |
944 | } | |
945 | ||
946 | return true | |
947 | } | |
948 | ||
949 | func (s *OutputState) deepcopy() *OutputState { | |
950 | if s == nil { | |
951 | return nil | |
952 | } | |
953 | ||
954 | stateCopy, err := copystructure.Config{Lock: true}.Copy(s) | |
955 | if err != nil { | |
956 | panic(fmt.Errorf("Error copying output value: %s", err)) | |
957 | } | |
958 | ||
959 | return stateCopy.(*OutputState) | |
960 | } | |
961 | ||
962 | // ModuleState is used to track all the state relevant to a single | |
963 | // module. Previous to Terraform 0.3, all state belonged to the "root" | |
964 | // module. | |
965 | type ModuleState struct { | |
966 | // Path is the import path from the root module. Modules imports are | |
967 | // always disjoint, so the path represents amodule tree | |
968 | Path []string `json:"path"` | |
969 | ||
15c0b25d AP |
970 | // Locals are kept only transiently in-memory, because we can always |
971 | // re-compute them. | |
972 | Locals map[string]interface{} `json:"-"` | |
973 | ||
bae9f6d2 JC |
974 | // Outputs declared by the module and maintained for each module |
975 | // even though only the root module technically needs to be kept. | |
976 | // This allows operators to inspect values at the boundaries. | |
977 | Outputs map[string]*OutputState `json:"outputs"` | |
978 | ||
979 | // Resources is a mapping of the logically named resource to | |
980 | // the state of the resource. Each resource may actually have | |
981 | // N instances underneath, although a user only needs to think | |
982 | // about the 1:1 case. | |
983 | Resources map[string]*ResourceState `json:"resources"` | |
984 | ||
985 | // Dependencies are a list of things that this module relies on | |
986 | // existing to remain intact. For example: an module may depend | |
987 | // on a VPC ID given by an aws_vpc resource. | |
988 | // | |
989 | // Terraform uses this information to build valid destruction | |
990 | // orders and to warn the user if they're destroying a module that | |
991 | // another resource depends on. | |
992 | // | |
993 | // Things can be put into this list that may not be managed by | |
994 | // Terraform. If Terraform doesn't find a matching ID in the | |
995 | // overall state, then it assumes it isn't managed and doesn't | |
996 | // worry about it. | |
997 | Dependencies []string `json:"depends_on"` | |
998 | ||
999 | mu sync.Mutex | |
1000 | } | |
1001 | ||
1002 | func (s *ModuleState) Lock() { s.mu.Lock() } | |
1003 | func (s *ModuleState) Unlock() { s.mu.Unlock() } | |
1004 | ||
1005 | // Equal tests whether one module state is equal to another. | |
1006 | func (m *ModuleState) Equal(other *ModuleState) bool { | |
1007 | m.Lock() | |
1008 | defer m.Unlock() | |
1009 | ||
1010 | // Paths must be equal | |
1011 | if !reflect.DeepEqual(m.Path, other.Path) { | |
1012 | return false | |
1013 | } | |
1014 | ||
1015 | // Outputs must be equal | |
1016 | if len(m.Outputs) != len(other.Outputs) { | |
1017 | return false | |
1018 | } | |
1019 | for k, v := range m.Outputs { | |
1020 | if !other.Outputs[k].Equal(v) { | |
1021 | return false | |
1022 | } | |
1023 | } | |
1024 | ||
1025 | // Dependencies must be equal. This sorts these in place but | |
1026 | // this shouldn't cause any problems. | |
1027 | sort.Strings(m.Dependencies) | |
1028 | sort.Strings(other.Dependencies) | |
1029 | if len(m.Dependencies) != len(other.Dependencies) { | |
1030 | return false | |
1031 | } | |
1032 | for i, d := range m.Dependencies { | |
1033 | if other.Dependencies[i] != d { | |
1034 | return false | |
1035 | } | |
1036 | } | |
1037 | ||
1038 | // Resources must be equal | |
1039 | if len(m.Resources) != len(other.Resources) { | |
1040 | return false | |
1041 | } | |
1042 | for k, r := range m.Resources { | |
1043 | otherR, ok := other.Resources[k] | |
1044 | if !ok { | |
1045 | return false | |
1046 | } | |
1047 | ||
1048 | if !r.Equal(otherR) { | |
1049 | return false | |
1050 | } | |
1051 | } | |
1052 | ||
1053 | return true | |
1054 | } | |
1055 | ||
1056 | // IsRoot says whether or not this module diff is for the root module. | |
1057 | func (m *ModuleState) IsRoot() bool { | |
1058 | m.Lock() | |
1059 | defer m.Unlock() | |
1060 | return reflect.DeepEqual(m.Path, rootModulePath) | |
1061 | } | |
1062 | ||
1063 | // IsDescendent returns true if other is a descendent of this module. | |
1064 | func (m *ModuleState) IsDescendent(other *ModuleState) bool { | |
1065 | m.Lock() | |
1066 | defer m.Unlock() | |
1067 | ||
1068 | i := len(m.Path) | |
1069 | return len(other.Path) > i && reflect.DeepEqual(other.Path[:i], m.Path) | |
1070 | } | |
1071 | ||
1072 | // Orphans returns a list of keys of resources that are in the State | |
1073 | // but aren't present in the configuration itself. Hence, these keys | |
1074 | // represent the state of resources that are orphans. | |
107c1cdb | 1075 | func (m *ModuleState) Orphans(c *configs.Module) []addrs.ResourceInstance { |
bae9f6d2 JC |
1076 | m.Lock() |
1077 | defer m.Unlock() | |
1078 | ||
107c1cdb | 1079 | inConfig := make(map[string]struct{}) |
bae9f6d2 | 1080 | if c != nil { |
107c1cdb ND |
1081 | for _, r := range c.ManagedResources { |
1082 | inConfig[r.Addr().String()] = struct{}{} | |
1083 | } | |
1084 | for _, r := range c.DataResources { | |
1085 | inConfig[r.Addr().String()] = struct{}{} | |
bae9f6d2 JC |
1086 | } |
1087 | } | |
1088 | ||
107c1cdb ND |
1089 | var result []addrs.ResourceInstance |
1090 | for k := range m.Resources { | |
1091 | // Since we've not yet updated state to use our new address format, | |
1092 | // we need to do some shimming here. | |
1093 | legacyAddr, err := parseResourceAddressInternal(k) | |
1094 | if err != nil { | |
1095 | // Suggests that the user tampered with the state, since we always | |
1096 | // generate valid internal addresses. | |
1097 | log.Printf("ModuleState has invalid resource key %q. Ignoring.", k) | |
1098 | continue | |
1099 | } | |
15c0b25d | 1100 | |
107c1cdb ND |
1101 | addr := legacyAddr.AbsResourceInstanceAddr().Resource |
1102 | compareKey := addr.Resource.String() // compare by resource address, ignoring instance key | |
1103 | if _, exists := inConfig[compareKey]; !exists { | |
1104 | result = append(result, addr) | |
1105 | } | |
1106 | } | |
15c0b25d AP |
1107 | return result |
1108 | } | |
1109 | ||
1110 | // RemovedOutputs returns a list of outputs that are in the State but aren't | |
1111 | // present in the configuration itself. | |
107c1cdb ND |
1112 | func (s *ModuleState) RemovedOutputs(outputs map[string]*configs.Output) []addrs.OutputValue { |
1113 | if outputs == nil { | |
1114 | // If we got no output map at all then we'll just treat our set of | |
1115 | // configured outputs as empty, since that suggests that they've all | |
1116 | // been removed by removing their containing module. | |
1117 | outputs = make(map[string]*configs.Output) | |
15c0b25d AP |
1118 | } |
1119 | ||
107c1cdb ND |
1120 | s.Lock() |
1121 | defer s.Unlock() | |
15c0b25d | 1122 | |
107c1cdb ND |
1123 | var ret []addrs.OutputValue |
1124 | for n := range s.Outputs { | |
1125 | if _, declared := outputs[n]; !declared { | |
1126 | ret = append(ret, addrs.OutputValue{ | |
1127 | Name: n, | |
1128 | }) | |
1129 | } | |
bae9f6d2 JC |
1130 | } |
1131 | ||
107c1cdb | 1132 | return ret |
bae9f6d2 JC |
1133 | } |
1134 | ||
1135 | // View returns a view with the given resource prefix. | |
1136 | func (m *ModuleState) View(id string) *ModuleState { | |
1137 | if m == nil { | |
1138 | return m | |
1139 | } | |
1140 | ||
1141 | r := m.deepcopy() | |
1142 | for k, _ := range r.Resources { | |
1143 | if id == k || strings.HasPrefix(k, id+".") { | |
1144 | continue | |
1145 | } | |
1146 | ||
1147 | delete(r.Resources, k) | |
1148 | } | |
1149 | ||
1150 | return r | |
1151 | } | |
1152 | ||
1153 | func (m *ModuleState) init() { | |
1154 | m.Lock() | |
1155 | defer m.Unlock() | |
1156 | ||
1157 | if m.Path == nil { | |
1158 | m.Path = []string{} | |
1159 | } | |
1160 | if m.Outputs == nil { | |
1161 | m.Outputs = make(map[string]*OutputState) | |
1162 | } | |
1163 | if m.Resources == nil { | |
1164 | m.Resources = make(map[string]*ResourceState) | |
1165 | } | |
1166 | ||
1167 | if m.Dependencies == nil { | |
1168 | m.Dependencies = make([]string, 0) | |
1169 | } | |
1170 | ||
1171 | for _, rs := range m.Resources { | |
1172 | rs.init() | |
1173 | } | |
1174 | } | |
1175 | ||
1176 | func (m *ModuleState) deepcopy() *ModuleState { | |
1177 | if m == nil { | |
1178 | return nil | |
1179 | } | |
1180 | ||
1181 | stateCopy, err := copystructure.Config{Lock: true}.Copy(m) | |
1182 | if err != nil { | |
1183 | panic(err) | |
1184 | } | |
1185 | ||
1186 | return stateCopy.(*ModuleState) | |
1187 | } | |
1188 | ||
1189 | // prune is used to remove any resources that are no longer required | |
1190 | func (m *ModuleState) prune() { | |
1191 | m.Lock() | |
1192 | defer m.Unlock() | |
1193 | ||
1194 | for k, v := range m.Resources { | |
1195 | if v == nil || (v.Primary == nil || v.Primary.ID == "") && len(v.Deposed) == 0 { | |
1196 | delete(m.Resources, k) | |
1197 | continue | |
1198 | } | |
1199 | ||
1200 | v.prune() | |
1201 | } | |
1202 | ||
1203 | for k, v := range m.Outputs { | |
1204 | if v.Value == config.UnknownVariableValue { | |
1205 | delete(m.Outputs, k) | |
1206 | } | |
1207 | } | |
1208 | ||
1209 | m.Dependencies = uniqueStrings(m.Dependencies) | |
1210 | } | |
1211 | ||
1212 | func (m *ModuleState) sort() { | |
1213 | for _, v := range m.Resources { | |
1214 | v.sort() | |
1215 | } | |
1216 | } | |
1217 | ||
1218 | func (m *ModuleState) String() string { | |
1219 | m.Lock() | |
1220 | defer m.Unlock() | |
1221 | ||
1222 | var buf bytes.Buffer | |
1223 | ||
1224 | if len(m.Resources) == 0 { | |
1225 | buf.WriteString("<no state>") | |
1226 | } | |
1227 | ||
1228 | names := make([]string, 0, len(m.Resources)) | |
1229 | for name, _ := range m.Resources { | |
1230 | names = append(names, name) | |
1231 | } | |
1232 | ||
1233 | sort.Sort(resourceNameSort(names)) | |
1234 | ||
1235 | for _, k := range names { | |
1236 | rs := m.Resources[k] | |
1237 | var id string | |
1238 | if rs.Primary != nil { | |
1239 | id = rs.Primary.ID | |
1240 | } | |
1241 | if id == "" { | |
1242 | id = "<not created>" | |
1243 | } | |
1244 | ||
1245 | taintStr := "" | |
1246 | if rs.Primary.Tainted { | |
1247 | taintStr = " (tainted)" | |
1248 | } | |
1249 | ||
1250 | deposedStr := "" | |
1251 | if len(rs.Deposed) > 0 { | |
1252 | deposedStr = fmt.Sprintf(" (%d deposed)", len(rs.Deposed)) | |
1253 | } | |
1254 | ||
1255 | buf.WriteString(fmt.Sprintf("%s:%s%s\n", k, taintStr, deposedStr)) | |
1256 | buf.WriteString(fmt.Sprintf(" ID = %s\n", id)) | |
1257 | if rs.Provider != "" { | |
1258 | buf.WriteString(fmt.Sprintf(" provider = %s\n", rs.Provider)) | |
1259 | } | |
1260 | ||
1261 | var attributes map[string]string | |
1262 | if rs.Primary != nil { | |
1263 | attributes = rs.Primary.Attributes | |
1264 | } | |
1265 | attrKeys := make([]string, 0, len(attributes)) | |
1266 | for ak, _ := range attributes { | |
1267 | if ak == "id" { | |
1268 | continue | |
1269 | } | |
1270 | ||
1271 | attrKeys = append(attrKeys, ak) | |
1272 | } | |
1273 | ||
1274 | sort.Strings(attrKeys) | |
1275 | ||
1276 | for _, ak := range attrKeys { | |
1277 | av := attributes[ak] | |
1278 | buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av)) | |
1279 | } | |
1280 | ||
1281 | for idx, t := range rs.Deposed { | |
1282 | taintStr := "" | |
1283 | if t.Tainted { | |
1284 | taintStr = " (tainted)" | |
1285 | } | |
1286 | buf.WriteString(fmt.Sprintf(" Deposed ID %d = %s%s\n", idx+1, t.ID, taintStr)) | |
1287 | } | |
1288 | ||
1289 | if len(rs.Dependencies) > 0 { | |
1290 | buf.WriteString(fmt.Sprintf("\n Dependencies:\n")) | |
1291 | for _, dep := range rs.Dependencies { | |
1292 | buf.WriteString(fmt.Sprintf(" %s\n", dep)) | |
1293 | } | |
1294 | } | |
1295 | } | |
1296 | ||
1297 | if len(m.Outputs) > 0 { | |
1298 | buf.WriteString("\nOutputs:\n\n") | |
1299 | ||
1300 | ks := make([]string, 0, len(m.Outputs)) | |
1301 | for k, _ := range m.Outputs { | |
1302 | ks = append(ks, k) | |
1303 | } | |
1304 | ||
1305 | sort.Strings(ks) | |
1306 | ||
1307 | for _, k := range ks { | |
1308 | v := m.Outputs[k] | |
1309 | switch vTyped := v.Value.(type) { | |
1310 | case string: | |
1311 | buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped)) | |
1312 | case []interface{}: | |
1313 | buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped)) | |
1314 | case map[string]interface{}: | |
1315 | var mapKeys []string | |
1316 | for key, _ := range vTyped { | |
1317 | mapKeys = append(mapKeys, key) | |
1318 | } | |
1319 | sort.Strings(mapKeys) | |
1320 | ||
1321 | var mapBuf bytes.Buffer | |
1322 | mapBuf.WriteString("{") | |
1323 | for _, key := range mapKeys { | |
1324 | mapBuf.WriteString(fmt.Sprintf("%s:%s ", key, vTyped[key])) | |
1325 | } | |
1326 | mapBuf.WriteString("}") | |
1327 | ||
1328 | buf.WriteString(fmt.Sprintf("%s = %s\n", k, mapBuf.String())) | |
1329 | } | |
1330 | } | |
1331 | } | |
1332 | ||
1333 | return buf.String() | |
1334 | } | |
1335 | ||
15c0b25d AP |
1336 | func (m *ModuleState) Empty() bool { |
1337 | return len(m.Locals) == 0 && len(m.Outputs) == 0 && len(m.Resources) == 0 | |
1338 | } | |
1339 | ||
bae9f6d2 JC |
1340 | // ResourceStateKey is a structured representation of the key used for the |
1341 | // ModuleState.Resources mapping | |
1342 | type ResourceStateKey struct { | |
1343 | Name string | |
1344 | Type string | |
1345 | Mode config.ResourceMode | |
1346 | Index int | |
1347 | } | |
1348 | ||
1349 | // Equal determines whether two ResourceStateKeys are the same | |
1350 | func (rsk *ResourceStateKey) Equal(other *ResourceStateKey) bool { | |
1351 | if rsk == nil || other == nil { | |
1352 | return false | |
1353 | } | |
1354 | if rsk.Mode != other.Mode { | |
1355 | return false | |
1356 | } | |
1357 | if rsk.Type != other.Type { | |
1358 | return false | |
1359 | } | |
1360 | if rsk.Name != other.Name { | |
1361 | return false | |
1362 | } | |
1363 | if rsk.Index != other.Index { | |
1364 | return false | |
1365 | } | |
1366 | return true | |
1367 | } | |
1368 | ||
1369 | func (rsk *ResourceStateKey) String() string { | |
1370 | if rsk == nil { | |
1371 | return "" | |
1372 | } | |
1373 | var prefix string | |
1374 | switch rsk.Mode { | |
1375 | case config.ManagedResourceMode: | |
1376 | prefix = "" | |
1377 | case config.DataResourceMode: | |
1378 | prefix = "data." | |
1379 | default: | |
1380 | panic(fmt.Errorf("unknown resource mode %s", rsk.Mode)) | |
1381 | } | |
1382 | if rsk.Index == -1 { | |
1383 | return fmt.Sprintf("%s%s.%s", prefix, rsk.Type, rsk.Name) | |
1384 | } | |
1385 | return fmt.Sprintf("%s%s.%s.%d", prefix, rsk.Type, rsk.Name, rsk.Index) | |
1386 | } | |
1387 | ||
1388 | // ParseResourceStateKey accepts a key in the format used by | |
1389 | // ModuleState.Resources and returns a resource name and resource index. In the | |
1390 | // state, a resource has the format "type.name.index" or "type.name". In the | |
1391 | // latter case, the index is returned as -1. | |
1392 | func ParseResourceStateKey(k string) (*ResourceStateKey, error) { | |
1393 | parts := strings.Split(k, ".") | |
1394 | mode := config.ManagedResourceMode | |
1395 | if len(parts) > 0 && parts[0] == "data" { | |
1396 | mode = config.DataResourceMode | |
1397 | // Don't need the constant "data" prefix for parsing | |
1398 | // now that we've figured out the mode. | |
1399 | parts = parts[1:] | |
1400 | } | |
1401 | if len(parts) < 2 || len(parts) > 3 { | |
1402 | return nil, fmt.Errorf("Malformed resource state key: %s", k) | |
1403 | } | |
1404 | rsk := &ResourceStateKey{ | |
1405 | Mode: mode, | |
1406 | Type: parts[0], | |
1407 | Name: parts[1], | |
1408 | Index: -1, | |
1409 | } | |
1410 | if len(parts) == 3 { | |
1411 | index, err := strconv.Atoi(parts[2]) | |
1412 | if err != nil { | |
1413 | return nil, fmt.Errorf("Malformed resource state key index: %s", k) | |
1414 | } | |
1415 | rsk.Index = index | |
1416 | } | |
1417 | return rsk, nil | |
1418 | } | |
1419 | ||
1420 | // ResourceState holds the state of a resource that is used so that | |
1421 | // a provider can find and manage an existing resource as well as for | |
1422 | // storing attributes that are used to populate variables of child | |
1423 | // resources. | |
1424 | // | |
1425 | // Attributes has attributes about the created resource that are | |
1426 | // queryable in interpolation: "${type.id.attr}" | |
1427 | // | |
1428 | // Extra is just extra data that a provider can return that we store | |
1429 | // for later, but is not exposed in any way to the user. | |
1430 | // | |
1431 | type ResourceState struct { | |
1432 | // This is filled in and managed by Terraform, and is the resource | |
1433 | // type itself such as "mycloud_instance". If a resource provider sets | |
1434 | // this value, it won't be persisted. | |
1435 | Type string `json:"type"` | |
1436 | ||
1437 | // Dependencies are a list of things that this resource relies on | |
1438 | // existing to remain intact. For example: an AWS instance might | |
1439 | // depend on a subnet (which itself might depend on a VPC, and so | |
1440 | // on). | |
1441 | // | |
1442 | // Terraform uses this information to build valid destruction | |
1443 | // orders and to warn the user if they're destroying a resource that | |
1444 | // another resource depends on. | |
1445 | // | |
1446 | // Things can be put into this list that may not be managed by | |
1447 | // Terraform. If Terraform doesn't find a matching ID in the | |
1448 | // overall state, then it assumes it isn't managed and doesn't | |
1449 | // worry about it. | |
1450 | Dependencies []string `json:"depends_on"` | |
1451 | ||
1452 | // Primary is the current active instance for this resource. | |
1453 | // It can be replaced but only after a successful creation. | |
1454 | // This is the instances on which providers will act. | |
1455 | Primary *InstanceState `json:"primary"` | |
1456 | ||
1457 | // Deposed is used in the mechanics of CreateBeforeDestroy: the existing | |
1458 | // Primary is Deposed to get it out of the way for the replacement Primary to | |
1459 | // be created by Apply. If the replacement Primary creates successfully, the | |
1460 | // Deposed instance is cleaned up. | |
1461 | // | |
1462 | // If there were problems creating the replacement Primary, the Deposed | |
1463 | // instance and the (now tainted) replacement Primary will be swapped so the | |
1464 | // tainted replacement will be cleaned up instead. | |
1465 | // | |
1466 | // An instance will remain in the Deposed list until it is successfully | |
1467 | // destroyed and purged. | |
1468 | Deposed []*InstanceState `json:"deposed"` | |
1469 | ||
1470 | // Provider is used when a resource is connected to a provider with an alias. | |
1471 | // If this string is empty, the resource is connected to the default provider, | |
1472 | // e.g. "aws_instance" goes with the "aws" provider. | |
1473 | // If the resource block contained a "provider" key, that value will be set here. | |
1474 | Provider string `json:"provider"` | |
1475 | ||
1476 | mu sync.Mutex | |
1477 | } | |
1478 | ||
1479 | func (s *ResourceState) Lock() { s.mu.Lock() } | |
1480 | func (s *ResourceState) Unlock() { s.mu.Unlock() } | |
1481 | ||
1482 | // Equal tests whether two ResourceStates are equal. | |
1483 | func (s *ResourceState) Equal(other *ResourceState) bool { | |
1484 | s.Lock() | |
1485 | defer s.Unlock() | |
1486 | ||
1487 | if s.Type != other.Type { | |
1488 | return false | |
1489 | } | |
1490 | ||
1491 | if s.Provider != other.Provider { | |
1492 | return false | |
1493 | } | |
1494 | ||
1495 | // Dependencies must be equal | |
1496 | sort.Strings(s.Dependencies) | |
1497 | sort.Strings(other.Dependencies) | |
1498 | if len(s.Dependencies) != len(other.Dependencies) { | |
1499 | return false | |
1500 | } | |
1501 | for i, d := range s.Dependencies { | |
1502 | if other.Dependencies[i] != d { | |
1503 | return false | |
1504 | } | |
1505 | } | |
1506 | ||
1507 | // States must be equal | |
1508 | if !s.Primary.Equal(other.Primary) { | |
1509 | return false | |
1510 | } | |
1511 | ||
1512 | return true | |
1513 | } | |
1514 | ||
1515 | // Taint marks a resource as tainted. | |
1516 | func (s *ResourceState) Taint() { | |
1517 | s.Lock() | |
1518 | defer s.Unlock() | |
1519 | ||
1520 | if s.Primary != nil { | |
1521 | s.Primary.Tainted = true | |
1522 | } | |
1523 | } | |
1524 | ||
1525 | // Untaint unmarks a resource as tainted. | |
1526 | func (s *ResourceState) Untaint() { | |
1527 | s.Lock() | |
1528 | defer s.Unlock() | |
1529 | ||
1530 | if s.Primary != nil { | |
1531 | s.Primary.Tainted = false | |
1532 | } | |
1533 | } | |
1534 | ||
107c1cdb ND |
1535 | // ProviderAddr returns the provider address for the receiver, by parsing the |
1536 | // string representation saved in state. An error can be returned if the | |
1537 | // value in state is corrupt. | |
1538 | func (s *ResourceState) ProviderAddr() (addrs.AbsProviderConfig, error) { | |
1539 | var diags tfdiags.Diagnostics | |
1540 | ||
1541 | str := s.Provider | |
1542 | traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) | |
1543 | diags = diags.Append(travDiags) | |
1544 | if travDiags.HasErrors() { | |
1545 | return addrs.AbsProviderConfig{}, diags.Err() | |
1546 | } | |
1547 | ||
1548 | addr, addrDiags := addrs.ParseAbsProviderConfig(traversal) | |
1549 | diags = diags.Append(addrDiags) | |
1550 | return addr, diags.Err() | |
1551 | } | |
1552 | ||
bae9f6d2 JC |
1553 | func (s *ResourceState) init() { |
1554 | s.Lock() | |
1555 | defer s.Unlock() | |
1556 | ||
1557 | if s.Primary == nil { | |
1558 | s.Primary = &InstanceState{} | |
1559 | } | |
1560 | s.Primary.init() | |
1561 | ||
1562 | if s.Dependencies == nil { | |
1563 | s.Dependencies = []string{} | |
1564 | } | |
1565 | ||
1566 | if s.Deposed == nil { | |
1567 | s.Deposed = make([]*InstanceState, 0) | |
1568 | } | |
1569 | } | |
1570 | ||
1571 | func (s *ResourceState) deepcopy() *ResourceState { | |
1572 | copy, err := copystructure.Config{Lock: true}.Copy(s) | |
1573 | if err != nil { | |
1574 | panic(err) | |
1575 | } | |
1576 | ||
1577 | return copy.(*ResourceState) | |
1578 | } | |
1579 | ||
1580 | // prune is used to remove any instances that are no longer required | |
1581 | func (s *ResourceState) prune() { | |
1582 | s.Lock() | |
1583 | defer s.Unlock() | |
1584 | ||
1585 | n := len(s.Deposed) | |
1586 | for i := 0; i < n; i++ { | |
1587 | inst := s.Deposed[i] | |
1588 | if inst == nil || inst.ID == "" { | |
1589 | copy(s.Deposed[i:], s.Deposed[i+1:]) | |
1590 | s.Deposed[n-1] = nil | |
1591 | n-- | |
1592 | i-- | |
1593 | } | |
1594 | } | |
1595 | s.Deposed = s.Deposed[:n] | |
1596 | ||
1597 | s.Dependencies = uniqueStrings(s.Dependencies) | |
1598 | } | |
1599 | ||
1600 | func (s *ResourceState) sort() { | |
1601 | s.Lock() | |
1602 | defer s.Unlock() | |
1603 | ||
1604 | sort.Strings(s.Dependencies) | |
1605 | } | |
1606 | ||
1607 | func (s *ResourceState) String() string { | |
1608 | s.Lock() | |
1609 | defer s.Unlock() | |
1610 | ||
1611 | var buf bytes.Buffer | |
1612 | buf.WriteString(fmt.Sprintf("Type = %s", s.Type)) | |
1613 | return buf.String() | |
1614 | } | |
1615 | ||
1616 | // InstanceState is used to track the unique state information belonging | |
1617 | // to a given instance. | |
1618 | type InstanceState struct { | |
1619 | // A unique ID for this resource. This is opaque to Terraform | |
1620 | // and is only meant as a lookup mechanism for the providers. | |
1621 | ID string `json:"id"` | |
1622 | ||
1623 | // Attributes are basic information about the resource. Any keys here | |
1624 | // are accessible in variable format within Terraform configurations: | |
1625 | // ${resourcetype.name.attribute}. | |
1626 | Attributes map[string]string `json:"attributes"` | |
1627 | ||
1628 | // Ephemeral is used to store any state associated with this instance | |
1629 | // that is necessary for the Terraform run to complete, but is not | |
1630 | // persisted to a state file. | |
1631 | Ephemeral EphemeralState `json:"-"` | |
1632 | ||
1633 | // Meta is a simple K/V map that is persisted to the State but otherwise | |
1634 | // ignored by Terraform core. It's meant to be used for accounting by | |
1635 | // external client code. The value here must only contain Go primitives | |
1636 | // and collections. | |
1637 | Meta map[string]interface{} `json:"meta"` | |
1638 | ||
1639 | // Tainted is used to mark a resource for recreation. | |
1640 | Tainted bool `json:"tainted"` | |
1641 | ||
1642 | mu sync.Mutex | |
1643 | } | |
1644 | ||
1645 | func (s *InstanceState) Lock() { s.mu.Lock() } | |
1646 | func (s *InstanceState) Unlock() { s.mu.Unlock() } | |
1647 | ||
1648 | func (s *InstanceState) init() { | |
1649 | s.Lock() | |
1650 | defer s.Unlock() | |
1651 | ||
1652 | if s.Attributes == nil { | |
1653 | s.Attributes = make(map[string]string) | |
1654 | } | |
1655 | if s.Meta == nil { | |
1656 | s.Meta = make(map[string]interface{}) | |
1657 | } | |
1658 | s.Ephemeral.init() | |
1659 | } | |
1660 | ||
107c1cdb ND |
1661 | // NewInstanceStateShimmedFromValue is a shim method to lower a new-style |
1662 | // object value representing the attributes of an instance object into the | |
1663 | // legacy InstanceState representation. | |
1664 | // | |
1665 | // This is for shimming to old components only and should not be used in new code. | |
1666 | func NewInstanceStateShimmedFromValue(state cty.Value, schemaVersion int) *InstanceState { | |
1667 | attrs := hcl2shim.FlatmapValueFromHCL2(state) | |
1668 | return &InstanceState{ | |
1669 | ID: attrs["id"], | |
1670 | Attributes: attrs, | |
1671 | Meta: map[string]interface{}{ | |
1672 | "schema_version": schemaVersion, | |
1673 | }, | |
1674 | } | |
1675 | } | |
1676 | ||
1677 | // AttrsAsObjectValue shims from the legacy InstanceState representation to | |
1678 | // a new-style cty object value representation of the state attributes, using | |
1679 | // the given type for guidance. | |
1680 | // | |
1681 | // The given type must be the implied type of the schema of the resource type | |
1682 | // of the object whose state is being converted, or the result is undefined. | |
1683 | // | |
1684 | // This is for shimming from old components only and should not be used in | |
1685 | // new code. | |
1686 | func (s *InstanceState) AttrsAsObjectValue(ty cty.Type) (cty.Value, error) { | |
1687 | if s == nil { | |
1688 | // if the state is nil, we need to construct a complete cty.Value with | |
1689 | // null attributes, rather than a single cty.NullVal(ty) | |
1690 | s = &InstanceState{} | |
1691 | } | |
1692 | ||
1693 | if s.Attributes == nil { | |
1694 | s.Attributes = map[string]string{} | |
1695 | } | |
1696 | ||
1697 | // make sure ID is included in the attributes. The InstanceState.ID value | |
1698 | // takes precedence. | |
1699 | if s.ID != "" { | |
1700 | s.Attributes["id"] = s.ID | |
1701 | } | |
1702 | ||
1703 | return hcl2shim.HCL2ValueFromFlatmap(s.Attributes, ty) | |
1704 | } | |
1705 | ||
bae9f6d2 JC |
1706 | // Copy all the Fields from another InstanceState |
1707 | func (s *InstanceState) Set(from *InstanceState) { | |
1708 | s.Lock() | |
1709 | defer s.Unlock() | |
1710 | ||
1711 | from.Lock() | |
1712 | defer from.Unlock() | |
1713 | ||
1714 | s.ID = from.ID | |
1715 | s.Attributes = from.Attributes | |
1716 | s.Ephemeral = from.Ephemeral | |
1717 | s.Meta = from.Meta | |
1718 | s.Tainted = from.Tainted | |
1719 | } | |
1720 | ||
1721 | func (s *InstanceState) DeepCopy() *InstanceState { | |
1722 | copy, err := copystructure.Config{Lock: true}.Copy(s) | |
1723 | if err != nil { | |
1724 | panic(err) | |
1725 | } | |
1726 | ||
1727 | return copy.(*InstanceState) | |
1728 | } | |
1729 | ||
1730 | func (s *InstanceState) Empty() bool { | |
1731 | if s == nil { | |
1732 | return true | |
1733 | } | |
1734 | s.Lock() | |
1735 | defer s.Unlock() | |
1736 | ||
1737 | return s.ID == "" | |
1738 | } | |
1739 | ||
1740 | func (s *InstanceState) Equal(other *InstanceState) bool { | |
1741 | // Short circuit some nil checks | |
1742 | if s == nil || other == nil { | |
1743 | return s == other | |
1744 | } | |
1745 | s.Lock() | |
1746 | defer s.Unlock() | |
1747 | ||
1748 | // IDs must be equal | |
1749 | if s.ID != other.ID { | |
1750 | return false | |
1751 | } | |
1752 | ||
1753 | // Attributes must be equal | |
1754 | if len(s.Attributes) != len(other.Attributes) { | |
1755 | return false | |
1756 | } | |
1757 | for k, v := range s.Attributes { | |
1758 | otherV, ok := other.Attributes[k] | |
1759 | if !ok { | |
1760 | return false | |
1761 | } | |
1762 | ||
1763 | if v != otherV { | |
1764 | return false | |
1765 | } | |
1766 | } | |
1767 | ||
1768 | // Meta must be equal | |
1769 | if len(s.Meta) != len(other.Meta) { | |
1770 | return false | |
1771 | } | |
1772 | if s.Meta != nil && other.Meta != nil { | |
1773 | // We only do the deep check if both are non-nil. If one is nil | |
1774 | // we treat it as equal since their lengths are both zero (check | |
1775 | // above). | |
15c0b25d AP |
1776 | // |
1777 | // Since this can contain numeric values that may change types during | |
1778 | // serialization, let's compare the serialized values. | |
1779 | sMeta, err := json.Marshal(s.Meta) | |
1780 | if err != nil { | |
1781 | // marshaling primitives shouldn't ever error out | |
1782 | panic(err) | |
1783 | } | |
1784 | otherMeta, err := json.Marshal(other.Meta) | |
1785 | if err != nil { | |
1786 | panic(err) | |
1787 | } | |
1788 | ||
1789 | if !bytes.Equal(sMeta, otherMeta) { | |
bae9f6d2 JC |
1790 | return false |
1791 | } | |
1792 | } | |
1793 | ||
1794 | if s.Tainted != other.Tainted { | |
1795 | return false | |
1796 | } | |
1797 | ||
1798 | return true | |
1799 | } | |
1800 | ||
1801 | // MergeDiff takes a ResourceDiff and merges the attributes into | |
1802 | // this resource state in order to generate a new state. This new | |
1803 | // state can be used to provide updated attribute lookups for | |
1804 | // variable interpolation. | |
1805 | // | |
1806 | // If the diff attribute requires computing the value, and hence | |
1807 | // won't be available until apply, the value is replaced with the | |
1808 | // computeID. | |
1809 | func (s *InstanceState) MergeDiff(d *InstanceDiff) *InstanceState { | |
1810 | result := s.DeepCopy() | |
1811 | if result == nil { | |
1812 | result = new(InstanceState) | |
1813 | } | |
1814 | result.init() | |
1815 | ||
1816 | if s != nil { | |
1817 | s.Lock() | |
1818 | defer s.Unlock() | |
1819 | for k, v := range s.Attributes { | |
1820 | result.Attributes[k] = v | |
1821 | } | |
1822 | } | |
1823 | if d != nil { | |
1824 | for k, diff := range d.CopyAttributes() { | |
1825 | if diff.NewRemoved { | |
1826 | delete(result.Attributes, k) | |
1827 | continue | |
1828 | } | |
1829 | if diff.NewComputed { | |
1830 | result.Attributes[k] = config.UnknownVariableValue | |
1831 | continue | |
1832 | } | |
1833 | ||
1834 | result.Attributes[k] = diff.New | |
1835 | } | |
1836 | } | |
1837 | ||
1838 | return result | |
1839 | } | |
1840 | ||
1841 | func (s *InstanceState) String() string { | |
107c1cdb ND |
1842 | notCreated := "<not created>" |
1843 | ||
1844 | if s == nil { | |
1845 | return notCreated | |
1846 | } | |
1847 | ||
bae9f6d2 JC |
1848 | s.Lock() |
1849 | defer s.Unlock() | |
1850 | ||
1851 | var buf bytes.Buffer | |
1852 | ||
107c1cdb ND |
1853 | if s.ID == "" { |
1854 | return notCreated | |
bae9f6d2 JC |
1855 | } |
1856 | ||
1857 | buf.WriteString(fmt.Sprintf("ID = %s\n", s.ID)) | |
1858 | ||
1859 | attributes := s.Attributes | |
1860 | attrKeys := make([]string, 0, len(attributes)) | |
1861 | for ak, _ := range attributes { | |
1862 | if ak == "id" { | |
1863 | continue | |
1864 | } | |
1865 | ||
1866 | attrKeys = append(attrKeys, ak) | |
1867 | } | |
1868 | sort.Strings(attrKeys) | |
1869 | ||
1870 | for _, ak := range attrKeys { | |
1871 | av := attributes[ak] | |
1872 | buf.WriteString(fmt.Sprintf("%s = %s\n", ak, av)) | |
1873 | } | |
1874 | ||
1875 | buf.WriteString(fmt.Sprintf("Tainted = %t\n", s.Tainted)) | |
1876 | ||
1877 | return buf.String() | |
1878 | } | |
1879 | ||
1880 | // EphemeralState is used for transient state that is only kept in-memory | |
1881 | type EphemeralState struct { | |
1882 | // ConnInfo is used for the providers to export information which is | |
1883 | // used to connect to the resource for provisioning. For example, | |
1884 | // this could contain SSH or WinRM credentials. | |
1885 | ConnInfo map[string]string `json:"-"` | |
1886 | ||
1887 | // Type is used to specify the resource type for this instance. This is only | |
1888 | // required for import operations (as documented). If the documentation | |
1889 | // doesn't state that you need to set this, then don't worry about | |
1890 | // setting it. | |
1891 | Type string `json:"-"` | |
1892 | } | |
1893 | ||
1894 | func (e *EphemeralState) init() { | |
1895 | if e.ConnInfo == nil { | |
1896 | e.ConnInfo = make(map[string]string) | |
1897 | } | |
1898 | } | |
1899 | ||
1900 | func (e *EphemeralState) DeepCopy() *EphemeralState { | |
1901 | copy, err := copystructure.Config{Lock: true}.Copy(e) | |
1902 | if err != nil { | |
1903 | panic(err) | |
1904 | } | |
1905 | ||
1906 | return copy.(*EphemeralState) | |
1907 | } | |
1908 | ||
1909 | type jsonStateVersionIdentifier struct { | |
1910 | Version int `json:"version"` | |
1911 | } | |
1912 | ||
1913 | // Check if this is a V0 format - the magic bytes at the start of the file | |
1914 | // should be "tfstate" if so. We no longer support upgrading this type of | |
1915 | // state but return an error message explaining to a user how they can | |
1916 | // upgrade via the 0.6.x series. | |
1917 | func testForV0State(buf *bufio.Reader) error { | |
1918 | start, err := buf.Peek(len("tfstate")) | |
1919 | if err != nil { | |
1920 | return fmt.Errorf("Failed to check for magic bytes: %v", err) | |
1921 | } | |
1922 | if string(start) == "tfstate" { | |
1923 | return fmt.Errorf("Terraform 0.7 no longer supports upgrading the binary state\n" + | |
1924 | "format which was used prior to Terraform 0.3. Please upgrade\n" + | |
1925 | "this state file using Terraform 0.6.16 prior to using it with\n" + | |
1926 | "Terraform 0.7.") | |
1927 | } | |
1928 | ||
1929 | return nil | |
1930 | } | |
1931 | ||
1932 | // ErrNoState is returned by ReadState when the io.Reader contains no data | |
1933 | var ErrNoState = errors.New("no state") | |
1934 | ||
1935 | // ReadState reads a state structure out of a reader in the format that | |
1936 | // was written by WriteState. | |
1937 | func ReadState(src io.Reader) (*State, error) { | |
15c0b25d AP |
1938 | // check for a nil file specifically, since that produces a platform |
1939 | // specific error if we try to use it in a bufio.Reader. | |
1940 | if f, ok := src.(*os.File); ok && f == nil { | |
1941 | return nil, ErrNoState | |
1942 | } | |
1943 | ||
bae9f6d2 | 1944 | buf := bufio.NewReader(src) |
15c0b25d | 1945 | |
bae9f6d2 | 1946 | if _, err := buf.Peek(1); err != nil { |
15c0b25d AP |
1947 | if err == io.EOF { |
1948 | return nil, ErrNoState | |
1949 | } | |
1950 | return nil, err | |
bae9f6d2 JC |
1951 | } |
1952 | ||
1953 | if err := testForV0State(buf); err != nil { | |
1954 | return nil, err | |
1955 | } | |
1956 | ||
1957 | // If we are JSON we buffer the whole thing in memory so we can read it twice. | |
1958 | // This is suboptimal, but will work for now. | |
1959 | jsonBytes, err := ioutil.ReadAll(buf) | |
1960 | if err != nil { | |
1961 | return nil, fmt.Errorf("Reading state file failed: %v", err) | |
1962 | } | |
1963 | ||
1964 | versionIdentifier := &jsonStateVersionIdentifier{} | |
1965 | if err := json.Unmarshal(jsonBytes, versionIdentifier); err != nil { | |
1966 | return nil, fmt.Errorf("Decoding state file version failed: %v", err) | |
1967 | } | |
1968 | ||
1969 | var result *State | |
1970 | switch versionIdentifier.Version { | |
1971 | case 0: | |
1972 | return nil, fmt.Errorf("State version 0 is not supported as JSON.") | |
1973 | case 1: | |
1974 | v1State, err := ReadStateV1(jsonBytes) | |
1975 | if err != nil { | |
1976 | return nil, err | |
1977 | } | |
1978 | ||
1979 | v2State, err := upgradeStateV1ToV2(v1State) | |
1980 | if err != nil { | |
1981 | return nil, err | |
1982 | } | |
1983 | ||
1984 | v3State, err := upgradeStateV2ToV3(v2State) | |
1985 | if err != nil { | |
1986 | return nil, err | |
1987 | } | |
1988 | ||
1989 | // increment the Serial whenever we upgrade state | |
1990 | v3State.Serial++ | |
1991 | result = v3State | |
1992 | case 2: | |
1993 | v2State, err := ReadStateV2(jsonBytes) | |
1994 | if err != nil { | |
1995 | return nil, err | |
1996 | } | |
1997 | v3State, err := upgradeStateV2ToV3(v2State) | |
1998 | if err != nil { | |
1999 | return nil, err | |
2000 | } | |
2001 | ||
2002 | v3State.Serial++ | |
2003 | result = v3State | |
2004 | case 3: | |
2005 | v3State, err := ReadStateV3(jsonBytes) | |
2006 | if err != nil { | |
2007 | return nil, err | |
2008 | } | |
2009 | ||
2010 | result = v3State | |
2011 | default: | |
2012 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | |
15c0b25d | 2013 | tfversion.SemVer.String(), versionIdentifier.Version) |
bae9f6d2 JC |
2014 | } |
2015 | ||
2016 | // If we reached this place we must have a result set | |
2017 | if result == nil { | |
2018 | panic("resulting state in load not set, assertion failed") | |
2019 | } | |
2020 | ||
2021 | // Prune the state when read it. Its possible to write unpruned states or | |
2022 | // for a user to make a state unpruned (nil-ing a module state for example). | |
2023 | result.prune() | |
2024 | ||
2025 | // Validate the state file is valid | |
2026 | if err := result.Validate(); err != nil { | |
2027 | return nil, err | |
2028 | } | |
2029 | ||
2030 | return result, nil | |
2031 | } | |
2032 | ||
2033 | func ReadStateV1(jsonBytes []byte) (*stateV1, error) { | |
2034 | v1State := &stateV1{} | |
2035 | if err := json.Unmarshal(jsonBytes, v1State); err != nil { | |
2036 | return nil, fmt.Errorf("Decoding state file failed: %v", err) | |
2037 | } | |
2038 | ||
2039 | if v1State.Version != 1 { | |
2040 | return nil, fmt.Errorf("Decoded state version did not match the decoder selection: "+ | |
2041 | "read %d, expected 1", v1State.Version) | |
2042 | } | |
2043 | ||
2044 | return v1State, nil | |
2045 | } | |
2046 | ||
2047 | func ReadStateV2(jsonBytes []byte) (*State, error) { | |
2048 | state := &State{} | |
2049 | if err := json.Unmarshal(jsonBytes, state); err != nil { | |
2050 | return nil, fmt.Errorf("Decoding state file failed: %v", err) | |
2051 | } | |
2052 | ||
2053 | // Check the version, this to ensure we don't read a future | |
2054 | // version that we don't understand | |
2055 | if state.Version > StateVersion { | |
2056 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | |
15c0b25d | 2057 | tfversion.SemVer.String(), state.Version) |
bae9f6d2 JC |
2058 | } |
2059 | ||
2060 | // Make sure the version is semantic | |
2061 | if state.TFVersion != "" { | |
2062 | if _, err := version.NewVersion(state.TFVersion); err != nil { | |
2063 | return nil, fmt.Errorf( | |
2064 | "State contains invalid version: %s\n\n"+ | |
2065 | "Terraform validates the version format prior to writing it. This\n"+ | |
2066 | "means that this is invalid of the state becoming corrupted through\n"+ | |
2067 | "some external means. Please manually modify the Terraform version\n"+ | |
2068 | "field to be a proper semantic version.", | |
2069 | state.TFVersion) | |
2070 | } | |
2071 | } | |
2072 | ||
2073 | // catch any unitialized fields in the state | |
2074 | state.init() | |
2075 | ||
2076 | // Sort it | |
2077 | state.sort() | |
2078 | ||
2079 | return state, nil | |
2080 | } | |
2081 | ||
2082 | func ReadStateV3(jsonBytes []byte) (*State, error) { | |
2083 | state := &State{} | |
2084 | if err := json.Unmarshal(jsonBytes, state); err != nil { | |
2085 | return nil, fmt.Errorf("Decoding state file failed: %v", err) | |
2086 | } | |
2087 | ||
2088 | // Check the version, this to ensure we don't read a future | |
2089 | // version that we don't understand | |
2090 | if state.Version > StateVersion { | |
2091 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | |
15c0b25d | 2092 | tfversion.SemVer.String(), state.Version) |
bae9f6d2 JC |
2093 | } |
2094 | ||
2095 | // Make sure the version is semantic | |
2096 | if state.TFVersion != "" { | |
2097 | if _, err := version.NewVersion(state.TFVersion); err != nil { | |
2098 | return nil, fmt.Errorf( | |
2099 | "State contains invalid version: %s\n\n"+ | |
2100 | "Terraform validates the version format prior to writing it. This\n"+ | |
2101 | "means that this is invalid of the state becoming corrupted through\n"+ | |
2102 | "some external means. Please manually modify the Terraform version\n"+ | |
2103 | "field to be a proper semantic version.", | |
2104 | state.TFVersion) | |
2105 | } | |
2106 | } | |
2107 | ||
2108 | // catch any unitialized fields in the state | |
2109 | state.init() | |
2110 | ||
2111 | // Sort it | |
2112 | state.sort() | |
2113 | ||
2114 | // Now we write the state back out to detect any changes in normaliztion. | |
2115 | // If our state is now written out differently, bump the serial number to | |
2116 | // prevent conflicts. | |
2117 | var buf bytes.Buffer | |
2118 | err := WriteState(state, &buf) | |
2119 | if err != nil { | |
2120 | return nil, err | |
2121 | } | |
2122 | ||
2123 | if !bytes.Equal(jsonBytes, buf.Bytes()) { | |
2124 | log.Println("[INFO] state modified during read or write. incrementing serial number") | |
2125 | state.Serial++ | |
2126 | } | |
2127 | ||
2128 | return state, nil | |
2129 | } | |
2130 | ||
2131 | // WriteState writes a state somewhere in a binary format. | |
2132 | func WriteState(d *State, dst io.Writer) error { | |
2133 | // writing a nil state is a noop. | |
2134 | if d == nil { | |
2135 | return nil | |
2136 | } | |
2137 | ||
2138 | // make sure we have no uninitialized fields | |
2139 | d.init() | |
2140 | ||
2141 | // Make sure it is sorted | |
2142 | d.sort() | |
2143 | ||
2144 | // Ensure the version is set | |
2145 | d.Version = StateVersion | |
2146 | ||
2147 | // If the TFVersion is set, verify it. We used to just set the version | |
2148 | // here, but this isn't safe since it changes the MD5 sum on some remote | |
2149 | // state storage backends such as Atlas. We now leave it be if needed. | |
2150 | if d.TFVersion != "" { | |
2151 | if _, err := version.NewVersion(d.TFVersion); err != nil { | |
2152 | return fmt.Errorf( | |
2153 | "Error writing state, invalid version: %s\n\n"+ | |
2154 | "The Terraform version when writing the state must be a semantic\n"+ | |
2155 | "version.", | |
2156 | d.TFVersion) | |
2157 | } | |
2158 | } | |
2159 | ||
2160 | // Encode the data in a human-friendly way | |
2161 | data, err := json.MarshalIndent(d, "", " ") | |
2162 | if err != nil { | |
2163 | return fmt.Errorf("Failed to encode state: %s", err) | |
2164 | } | |
2165 | ||
2166 | // We append a newline to the data because MarshalIndent doesn't | |
2167 | data = append(data, '\n') | |
2168 | ||
2169 | // Write the data out to the dst | |
2170 | if _, err := io.Copy(dst, bytes.NewReader(data)); err != nil { | |
2171 | return fmt.Errorf("Failed to write state: %v", err) | |
2172 | } | |
2173 | ||
2174 | return nil | |
2175 | } | |
2176 | ||
2177 | // resourceNameSort implements the sort.Interface to sort name parts lexically for | |
2178 | // strings and numerically for integer indexes. | |
2179 | type resourceNameSort []string | |
2180 | ||
2181 | func (r resourceNameSort) Len() int { return len(r) } | |
2182 | func (r resourceNameSort) Swap(i, j int) { r[i], r[j] = r[j], r[i] } | |
2183 | ||
2184 | func (r resourceNameSort) Less(i, j int) bool { | |
2185 | iParts := strings.Split(r[i], ".") | |
2186 | jParts := strings.Split(r[j], ".") | |
2187 | ||
2188 | end := len(iParts) | |
2189 | if len(jParts) < end { | |
2190 | end = len(jParts) | |
2191 | } | |
2192 | ||
2193 | for idx := 0; idx < end; idx++ { | |
2194 | if iParts[idx] == jParts[idx] { | |
2195 | continue | |
2196 | } | |
2197 | ||
2198 | // sort on the first non-matching part | |
2199 | iInt, iIntErr := strconv.Atoi(iParts[idx]) | |
2200 | jInt, jIntErr := strconv.Atoi(jParts[idx]) | |
2201 | ||
2202 | switch { | |
2203 | case iIntErr == nil && jIntErr == nil: | |
2204 | // sort numerically if both parts are integers | |
2205 | return iInt < jInt | |
2206 | case iIntErr == nil: | |
2207 | // numbers sort before strings | |
2208 | return true | |
2209 | case jIntErr == nil: | |
2210 | return false | |
2211 | default: | |
2212 | return iParts[idx] < jParts[idx] | |
2213 | } | |
2214 | } | |
2215 | ||
2216 | return r[i] < r[j] | |
2217 | } | |
2218 | ||
2219 | // moduleStateSort implements sort.Interface to sort module states | |
2220 | type moduleStateSort []*ModuleState | |
2221 | ||
2222 | func (s moduleStateSort) Len() int { | |
2223 | return len(s) | |
2224 | } | |
2225 | ||
2226 | func (s moduleStateSort) Less(i, j int) bool { | |
2227 | a := s[i] | |
2228 | b := s[j] | |
2229 | ||
2230 | // If either is nil, then the nil one is "less" than | |
2231 | if a == nil || b == nil { | |
2232 | return a == nil | |
2233 | } | |
2234 | ||
2235 | // If the lengths are different, then the shorter one always wins | |
2236 | if len(a.Path) != len(b.Path) { | |
2237 | return len(a.Path) < len(b.Path) | |
2238 | } | |
2239 | ||
2240 | // Otherwise, compare lexically | |
2241 | return strings.Join(a.Path, ".") < strings.Join(b.Path, ".") | |
2242 | } | |
2243 | ||
2244 | func (s moduleStateSort) Swap(i, j int) { | |
2245 | s[i], s[j] = s[j], s[i] | |
2246 | } | |
2247 | ||
2248 | const stateValidateErrMultiModule = ` | |
2249 | Multiple modules with the same path: %s | |
2250 | ||
2251 | This means that there are multiple entries in the "modules" field | |
2252 | in your state file that point to the same module. This will cause Terraform | |
2253 | to behave in unexpected and error prone ways and is invalid. Please back up | |
2254 | and modify your state file manually to resolve this. | |
2255 | ` |