diff --git a/terraform/state.go b/terraform/state.go index 7a23d4c26..2ab56ba6c 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -98,8 +98,14 @@ func NewState() *State { // the given path. If the path is "root", for example, then children // returned might be "root.child", but not "root.child.grandchild". func (s *State) Children(path []string) []*ModuleState { + s.Lock() + defer s.Unlock() // TODO: test + return s.children(path) +} + +func (s *State) children(path []string) []*ModuleState { result := make([]*ModuleState, 0) for _, m := range s.Modules { if len(m.Path) != len(path)+1 { @@ -120,8 +126,15 @@ func (s *State) Children(path []string) []*ModuleState { // This should be the preferred method to add module states since it // allows us to optimize lookups later as well as control sorting. func (s *State) AddModule(path []string) *ModuleState { + s.Lock() + defer s.Unlock() + + return s.addModule(path) +} + +func (s *State) addModule(path []string) *ModuleState { // check if the module exists first - m := s.ModuleByPath(path) + m := s.moduleByPath(path) if m != nil { return m } @@ -140,6 +153,13 @@ func (s *State) ModuleByPath(path []string) *ModuleState { if s == nil { return nil } + s.Lock() + defer s.Unlock() + + return s.moduleByPath(path) +} + +func (s *State) moduleByPath(path []string) *ModuleState { for _, mod := range s.Modules { if mod.Path == nil { panic("missing module path") @@ -155,6 +175,14 @@ func (s *State) ModuleByPath(path []string) *ModuleState { // returning their full paths. These paths can be used with ModuleByPath // to return the actual state. func (s *State) ModuleOrphans(path []string, c *config.Config) [][]string { + s.Lock() + defer s.Unlock() + + return s.moduleOrphans(path, c) + +} + +func (s *State) moduleOrphans(path []string, c *config.Config) [][]string { // direct keeps track of what direct children we have both in our config // and in our state. childrenKeys keeps track of what isn't an orphan. direct := make(map[string]struct{}) @@ -168,7 +196,7 @@ func (s *State) ModuleOrphans(path []string, c *config.Config) [][]string { // Go over the direct children and find any that aren't in our keys. var orphans [][]string - for _, m := range s.Children(path) { + for _, m := range s.children(path) { key := m.Path[len(m.Path)-1] // Record that we found this key as a direct child. We use this @@ -228,6 +256,8 @@ func (s *State) Empty() bool { if s == nil { return true } + s.Lock() + defer s.Unlock() return len(s.Modules) == 0 } @@ -238,6 +268,9 @@ func (s *State) IsRemote() bool { if s == nil { return false } + s.Lock() + defer s.Unlock() + if s.Remote == nil { return false } @@ -258,6 +291,9 @@ func (s *State) IsRemote() bool { // If this returns an error, then the user should be notified. The error // response will include detailed information on the nature of the error. func (s *State) Validate() error { + s.Lock() + defer s.Unlock() + var result error // !!!! FOR DEVELOPERS !!!! @@ -295,6 +331,9 @@ func (s *State) Validate() error { // all children as well. To check what will be deleted, use a StateFilter // first. func (s *State) Remove(addr ...string) error { + s.Lock() + defer s.Unlock() + // Filter out what we need to delete filter := &StateFilter{State: s} results, err := filter.Filter(addr...) @@ -362,7 +401,7 @@ func (s *State) removeModule(path []string, v *ModuleState) { func (s *State) removeResource(path []string, v *ResourceState) { // Get the module this resource lives in. If it doesn't exist, we're done. - mod := s.ModuleByPath(path) + mod := s.moduleByPath(path) if mod == nil { return } @@ -420,6 +459,16 @@ func (s *State) Equal(other *State) bool { return s == other } + s.Lock() + defer s.Unlock() + return s.equal(other) +} + +func (s *State) equal(other *State) bool { + if s == nil || other == nil { + return s == other + } + // If the versions are different, they're certainly not equal if s.Version != other.Version { return false @@ -431,7 +480,7 @@ func (s *State) Equal(other *State) bool { } for _, m := range s.Modules { // This isn't very optimal currently but works. - otherM := other.ModuleByPath(m.Path) + otherM := other.moduleByPath(m.Path) if otherM == nil { return false } @@ -466,7 +515,6 @@ const ( // An error is returned if the two states are not of the same lineage, // in which case the integer returned has no meaning. func (s *State) CompareAges(other *State) (StateAgeComparison, error) { - // nil states are "older" than actual states switch { case s != nil && other == nil: @@ -483,6 +531,9 @@ func (s *State) CompareAges(other *State) (StateAgeComparison, error) { ) } + s.Lock() + defer s.Unlock() + switch { case s.Serial < other.Serial: return StateAgeReceiverOlder, nil @@ -496,6 +547,9 @@ func (s *State) CompareAges(other *State) (StateAgeComparison, error) { // SameLineage returns true only if the state given in argument belongs // to the same "lineage" of states as the reciever. func (s *State) SameLineage(other *State) bool { + s.Lock() + defer s.Unlock() + // If one of the states has no lineage then it is assumed to predate // this concept, and so we'll accept it as belonging to any lineage // so that a lineage string can be assigned to newer versions @@ -527,10 +581,13 @@ func (s *State) IncrementSerialMaybe(other *State) { if other == nil { return } + s.Lock() + defer s.Unlock() + if s.Serial > other.Serial { return } - if other.TFVersion != s.TFVersion || !s.Equal(other) { + if other.TFVersion != s.TFVersion || !s.equal(other) { if other.Serial > s.Serial { s.Serial = other.Serial } @@ -542,6 +599,9 @@ func (s *State) IncrementSerialMaybe(other *State) { // FromFutureTerraform checks if this state was written by a Terraform // version from the future. func (s *State) FromFutureTerraform() bool { + s.Lock() + defer s.Unlock() + // No TF version means it is certainly from the past if s.TFVersion == "" { return false @@ -552,6 +612,8 @@ func (s *State) FromFutureTerraform() bool { } func (s *State) Init() { + s.Lock() + defer s.Unlock() s.init() } @@ -559,10 +621,10 @@ func (s *State) init() { if s.Version == 0 { s.Version = StateVersion } - if s.ModuleByPath(rootModulePath) == nil { - s.AddModule(rootModulePath) + if s.moduleByPath(rootModulePath) == nil { + s.addModule(rootModulePath) } - s.EnsureHasLineage() + s.ensureHasLineage() for _, mod := range s.Modules { mod.init() @@ -574,6 +636,13 @@ func (s *State) init() { } func (s *State) EnsureHasLineage() { + s.Lock() + defer s.Unlock() + + s.ensureHasLineage() +} + +func (s *State) ensureHasLineage() { if s.Lineage == "" { s.Lineage = uuid.NewV4().String() log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) @@ -585,7 +654,13 @@ func (s *State) EnsureHasLineage() { // AddModuleState insert this module state and override any existing ModuleState func (s *State) AddModuleState(mod *ModuleState) { mod.init() + s.Lock() + defer s.Unlock() + s.addModuleState(mod) +} + +func (s *State) addModuleState(mod *ModuleState) { for i, m := range s.Modules { if reflect.DeepEqual(m.Path, mod.Path) { s.Modules[i] = mod @@ -624,6 +699,8 @@ func (s *State) String() string { if s == nil { return "" } + s.Lock() + defer s.Unlock() var buf bytes.Buffer for _, m := range s.Modules { @@ -691,10 +768,19 @@ func (r *RemoteState) deepcopy() *RemoteState { } func (r *RemoteState) Empty() bool { - return r == nil || r.Type == "" + if r == nil { + return true + } + r.Lock() + defer r.Unlock() + + return r.Type == "" } func (r *RemoteState) Equals(other *RemoteState) bool { + r.Lock() + defer r.Unlock() + if r.Type != other.Type { return false } @@ -741,6 +827,8 @@ func (s *OutputState) Equal(other *OutputState) bool { if s == nil || other == nil { return false } + s.Lock() + defer s.Unlock() if s.Type != other.Type { return false @@ -811,6 +899,9 @@ func (s *ModuleState) Unlock() { s.mu.Unlock() } // Equal tests whether one module state is equal to another. func (m *ModuleState) Equal(other *ModuleState) bool { + m.Lock() + defer m.Unlock() + // Paths must be equal if !reflect.DeepEqual(m.Path, other.Path) { return false @@ -859,11 +950,16 @@ func (m *ModuleState) Equal(other *ModuleState) bool { // IsRoot says whether or not this module diff is for the root module. func (m *ModuleState) IsRoot() bool { + m.Lock() + defer m.Unlock() return reflect.DeepEqual(m.Path, rootModulePath) } // IsDescendent returns true if other is a descendent of this module. func (m *ModuleState) IsDescendent(other *ModuleState) bool { + m.Lock() + defer m.Unlock() + i := len(m.Path) return len(other.Path) > i && reflect.DeepEqual(other.Path[:i], m.Path) } @@ -872,6 +968,9 @@ func (m *ModuleState) IsDescendent(other *ModuleState) bool { // but aren't present in the configuration itself. Hence, these keys // represent the state of resources that are orphans. func (m *ModuleState) Orphans(c *config.Config) []string { + m.Lock() + defer m.Unlock() + keys := make(map[string]struct{}) for k, _ := range m.Resources { keys[k] = struct{}{} @@ -953,6 +1052,9 @@ func (m *ModuleState) deepcopy() *ModuleState { // prune is used to remove any resources that are no longer required func (m *ModuleState) prune() { + m.Lock() + defer m.Unlock() + for k, v := range m.Resources { v.prune() @@ -975,6 +1077,9 @@ func (m *ModuleState) sort() { } func (m *ModuleState) String() string { + m.Lock() + defer m.Unlock() + var buf bytes.Buffer if len(m.Resources) == 0 { @@ -1230,6 +1335,9 @@ func (s *ResourceState) Unlock() { s.mu.Unlock() } // Equal tests whether two ResourceStates are equal. func (s *ResourceState) Equal(other *ResourceState) bool { + s.Lock() + defer s.Unlock() + if s.Type != other.Type { return false } @@ -1259,43 +1367,49 @@ func (s *ResourceState) Equal(other *ResourceState) bool { } // Taint marks a resource as tainted. -func (r *ResourceState) Taint() { - if r.Primary != nil { - r.Primary.Tainted = true +func (s *ResourceState) Taint() { + s.Lock() + defer s.Unlock() + + if s.Primary != nil { + s.Primary.Tainted = true } } // Untaint unmarks a resource as tainted. -func (r *ResourceState) Untaint() { - if r.Primary != nil { - r.Primary.Tainted = false +func (s *ResourceState) Untaint() { + s.Lock() + defer s.Unlock() + + if s.Primary != nil { + s.Primary.Tainted = false } } -func (r *ResourceState) init() { - r.Lock() - defer r.Unlock() +func (s *ResourceState) init() { + s.Lock() + defer s.Unlock() - if r.Primary == nil { - r.Primary = &InstanceState{} + if s.Primary == nil { + s.Primary = &InstanceState{} } - r.Primary.init() + s.Primary.init() - if r.Dependencies == nil { - r.Dependencies = []string{} + if s.Dependencies == nil { + s.Dependencies = []string{} } - if r.Deposed == nil { - r.Deposed = make([]*InstanceState, 0) + if s.Deposed == nil { + s.Deposed = make([]*InstanceState, 0) } - for _, dep := range r.Deposed { + for _, dep := range s.Deposed { dep.init() } } -func (r *ResourceState) deepcopy() *ResourceState { - copy, err := copystructure.Config{Lock: true}.Copy(r) +func (s *ResourceState) deepcopy() *ResourceState { + copy, err := copystructure.Config{Lock: true}.Copy(s) if err != nil { panic(err) } @@ -1304,26 +1418,35 @@ func (r *ResourceState) deepcopy() *ResourceState { } // prune is used to remove any instances that are no longer required -func (r *ResourceState) prune() { - n := len(r.Deposed) +func (s *ResourceState) prune() { + s.Lock() + defer s.Unlock() + + n := len(s.Deposed) for i := 0; i < n; i++ { - inst := r.Deposed[i] + inst := s.Deposed[i] if inst == nil || inst.ID == "" { - copy(r.Deposed[i:], r.Deposed[i+1:]) - r.Deposed[n-1] = nil + copy(s.Deposed[i:], s.Deposed[i+1:]) + s.Deposed[n-1] = nil n-- i-- } } - r.Deposed = r.Deposed[:n] + s.Deposed = s.Deposed[:n] } -func (r *ResourceState) sort() { - sort.Strings(r.Dependencies) +func (s *ResourceState) sort() { + s.Lock() + defer s.Unlock() + + sort.Strings(s.Dependencies) } func (s *ResourceState) String() string { + s.Lock() + defer s.Unlock() + var buf bytes.Buffer buf.WriteString(fmt.Sprintf("Type = %s", s.Type)) return buf.String() @@ -1360,36 +1483,36 @@ type InstanceState struct { func (s *InstanceState) Lock() { s.mu.Lock() } func (s *InstanceState) Unlock() { s.mu.Unlock() } -func (i *InstanceState) init() { - i.Lock() - defer i.Unlock() +func (s *InstanceState) init() { + s.Lock() + defer s.Unlock() - if i.Attributes == nil { - i.Attributes = make(map[string]string) + if s.Attributes == nil { + s.Attributes = make(map[string]string) } - if i.Meta == nil { - i.Meta = make(map[string]string) + if s.Meta == nil { + s.Meta = make(map[string]string) } - i.Ephemeral.init() + s.Ephemeral.init() } // Copy all the Fields from another InstanceState -func (i *InstanceState) Set(from *InstanceState) { - i.Lock() - defer i.Unlock() +func (s *InstanceState) Set(from *InstanceState) { + s.Lock() + defer s.Unlock() from.Lock() defer from.Unlock() - i.ID = from.ID - i.Attributes = from.Attributes - i.Ephemeral = from.Ephemeral - i.Meta = from.Meta - i.Tainted = from.Tainted + s.ID = from.ID + s.Attributes = from.Attributes + s.Ephemeral = from.Ephemeral + s.Meta = from.Meta + s.Tainted = from.Tainted } -func (i *InstanceState) DeepCopy() *InstanceState { - copy, err := copystructure.Config{Lock: true}.Copy(i) +func (s *InstanceState) DeepCopy() *InstanceState { + copy, err := copystructure.Config{Lock: true}.Copy(s) if err != nil { panic(err) } @@ -1398,7 +1521,13 @@ func (i *InstanceState) DeepCopy() *InstanceState { } func (s *InstanceState) Empty() bool { - return s == nil || s.ID == "" + if s == nil { + return true + } + s.Lock() + defer s.Unlock() + + return s.ID == "" } func (s *InstanceState) Equal(other *InstanceState) bool { @@ -1406,6 +1535,8 @@ func (s *InstanceState) Equal(other *InstanceState) bool { if s == nil || other == nil { return s == other } + s.Lock() + defer s.Unlock() // IDs must be equal if s.ID != other.ID { @@ -1465,6 +1596,8 @@ func (s *InstanceState) MergeDiff(d *InstanceDiff) *InstanceState { result.init() if s != nil { + s.Lock() + defer s.Unlock() for k, v := range s.Attributes { result.Attributes[k] = v } @@ -1487,16 +1620,19 @@ func (s *InstanceState) MergeDiff(d *InstanceDiff) *InstanceState { return result } -func (i *InstanceState) String() string { +func (s *InstanceState) String() string { + s.Lock() + defer s.Unlock() + var buf bytes.Buffer - if i == nil || i.ID == "" { + if s == nil || s.ID == "" { return "" } - buf.WriteString(fmt.Sprintf("ID = %s\n", i.ID)) + buf.WriteString(fmt.Sprintf("ID = %s\n", s.ID)) - attributes := i.Attributes + attributes := s.Attributes attrKeys := make([]string, 0, len(attributes)) for ak, _ := range attributes { if ak == "id" { @@ -1512,7 +1648,7 @@ func (i *InstanceState) String() string { buf.WriteString(fmt.Sprintf("%s = %s\n", ak, av)) } - buf.WriteString(fmt.Sprintf("Tainted = %t\n", i.Tainted)) + buf.WriteString(fmt.Sprintf("Tainted = %t\n", s.Tainted)) return buf.String() }