state: only change serial if changed

This commit is contained in:
Mitchell Hashimoto 2015-02-23 21:26:33 -08:00
parent bfe0edef51
commit c2bf600603
6 changed files with 64 additions and 22 deletions

View File

@ -104,6 +104,8 @@ func (s *CacheState) RefreshState() error {
s.refreshResult = CacheRefreshNoop s.refreshResult = CacheRefreshNoop
return err return err
} }
cached = durable
} }
s.state = cached s.state = cached

View File

@ -18,6 +18,7 @@ func (s *InmemState) RefreshState() error {
} }
func (s *InmemState) WriteState(state *terraform.State) error { func (s *InmemState) WriteState(state *terraform.State) error {
state.IncrementSerialMaybe(s.state)
s.state = state s.state = state
return nil return nil
} }

View File

@ -15,13 +15,15 @@ type LocalState struct {
Path string Path string
PathOut string PathOut string
state *terraform.State state *terraform.State
written bool readState *terraform.State
written bool
} }
// SetState will force a specific state in-memory for this local state. // SetState will force a specific state in-memory for this local state.
func (s *LocalState) SetState(state *terraform.State) { func (s *LocalState) SetState(state *terraform.State) {
s.state = state s.state = state
s.readState = state
} }
// StateReader impl. // StateReader impl.
@ -61,6 +63,9 @@ func (s *LocalState) WriteState(state *terraform.State) error {
} }
defer f.Close() defer f.Close()
s.state.IncrementSerialMaybe(s.readState)
s.readState = s.state
if err := terraform.WriteState(s.state, f); err != nil { if err := terraform.WriteState(s.state, f); err != nil {
return err return err
} }
@ -105,5 +110,6 @@ func (s *LocalState) RefreshState() error {
} }
s.state = state s.state = state
s.readState = state
return nil return nil
} }

View File

@ -28,9 +28,7 @@ func TestState(t *testing.T, s interface{}) {
current := TestStateInitial() current := TestStateInitial()
// Check that the initial state is correct // Check that the initial state is correct
state := reader.State() if state := reader.State(); !reflect.DeepEqual(state, current) {
current.Serial = state.Serial
if !reflect.DeepEqual(state, current) {
t.Fatalf("not initial: %#v\n\n%#v", state, current) t.Fatalf("not initial: %#v\n\n%#v", state, current)
} }
@ -67,11 +65,49 @@ func TestState(t *testing.T, s interface{}) {
// Just set the serials the same... Then compare. // Just set the serials the same... Then compare.
actual := reader.State() actual := reader.State()
actual.Serial = current.Serial
if !reflect.DeepEqual(actual, current) { if !reflect.DeepEqual(actual, current) {
t.Fatalf("bad: %#v\n\n%#v", actual, current) t.Fatalf("bad: %#v\n\n%#v", actual, current)
} }
} }
// If we can write and persist then verify that the serial
// is only implemented on change.
writer, writeOk := s.(StateWriter)
persister, persistOk := s.(StatePersister)
if writeOk && persistOk {
// Same serial
serial := current.Serial
if err := writer.WriteState(current); err != nil {
t.Fatalf("err: %s", err)
}
if err := persister.PersistState(); err != nil {
t.Fatalf("err: %s", err)
}
if reader.State().Serial != serial {
t.Fatalf("bad: expected %d, got %d", serial, reader.State().Serial)
}
// Change the serial
currentCopy := *current
current = &currentCopy
current.Modules = []*terraform.ModuleState{
&terraform.ModuleState{
Path: []string{"root", "somewhere"},
Outputs: map[string]string{"serialCheck": "true"},
},
}
if err := writer.WriteState(current); err != nil {
t.Fatalf("err: %s", err)
}
if err := persister.PersistState(); err != nil {
t.Fatalf("err: %s", err)
}
if reader.State().Serial <= serial {
t.Fatalf("bad: expected %d, got %d", serial, reader.State().Serial)
}
}
} }
// TestStateInitial is the initial state that a State should have // TestStateInitial is the initial state that a State should have

View File

@ -161,6 +161,11 @@ func (s *State) RootModule() *ModuleState {
// Equal tests if one state is equal to another. // Equal tests if one state is equal to another.
func (s *State) Equal(other *State) bool { func (s *State) Equal(other *State) bool {
// If one is nil, we do a direct check
if s == nil || other == nil {
return s == other
}
// If the versions are different, they're certainly not equal // If the versions are different, they're certainly not equal
if s.Version != other.Version { if s.Version != other.Version {
return false return false
@ -183,6 +188,14 @@ func (s *State) Equal(other *State) bool {
return true return true
} }
// IncrementSerialMaybe increments the serial number of this state
// if it different from the other state.
func (s *State) IncrementSerialMaybe(other *State) {
if !s.Equal(other) {
s.Serial++
}
}
func (s *State) init() { func (s *State) init() {
if s.Version == 0 { if s.Version == 0 {
s.Version = StateVersion s.Version = StateVersion
@ -951,9 +964,6 @@ func WriteState(d *State, dst io.Writer) error {
// Ensure the version is set // Ensure the version is set
d.Version = StateVersion d.Version = StateVersion
// Always increment the serial number
d.Serial++
// Encode the data in a human-friendly way // Encode the data in a human-friendly way
data, err := json.MarshalIndent(d, "", " ") data, err := json.MarshalIndent(d, "", " ")
if err != nil { if err != nil {

View File

@ -537,15 +537,6 @@ func TestReadWriteState(t *testing.T) {
t.Fatalf("bad version number: %d", state.Version) t.Fatalf("bad version number: %d", state.Version)
} }
// Verify the serial number is incremented
if state.Serial != 10 {
t.Fatalf("bad serial: %d", state.Serial)
}
// Remove the changes or the checksum will fail
state.Version = 0
state.Serial = 9
// Checksum after the write // Checksum after the write
chksumAfter := checksumStruct(t, state) chksumAfter := checksumStruct(t, state)
if chksumAfter != chksum { if chksumAfter != chksum {
@ -557,10 +548,6 @@ func TestReadWriteState(t *testing.T) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
// Verify the changes came through
state.Version = StateVersion
state.Serial = 10
// ReadState should not restore sensitive information! // ReadState should not restore sensitive information!
mod := state.RootModule() mod := state.RootModule()
mod.Resources["foo"].Primary.Ephemeral = EphemeralState{} mod.Resources["foo"].Primary.Ephemeral = EphemeralState{}