Skip to content

Commit

Permalink
states: RefreshStateWithoutCheckVersion
Browse files Browse the repository at this point in the history
Add RefreshStateWithoutCheckVersion method to the statemgr.Persistent
interface, allowing callers to refresh state from the backend without
raising errors if the state's Terraform version is thought to be
not fully compatible.

This enables use cases where we can be extremely confident that any
state file we can read is suitable, such as the Terraform provider's
remote state data source, which only reads outputs.
  • Loading branch information
alisdair committed Oct 26, 2020
1 parent ff73826 commit 06af227
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 11 deletions.
18 changes: 13 additions & 5 deletions states/remote/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,19 @@ func (s *State) WriteStateForMigration(f *statefile.File, force bool) error {
func (s *State) RefreshState() error {
s.mu.Lock()
defer s.mu.Unlock()
return s.refreshState()
return s.refreshState(true)
}

func (s *State) RefreshStateWithoutCheckVersion() error {
s.mu.Lock()
defer s.mu.Unlock()
return s.refreshState(false)
}

// refreshState is the main implementation of RefreshState, but split out so
// that we can make internal calls to it from methods that are already holding
// the s.mu lock.
func (s *State) refreshState() error {
func (s *State) refreshState(checkVersion bool) error {
payload, err := s.Client.Get()
if err != nil {
return err
Expand All @@ -125,8 +131,10 @@ func (s *State) refreshState() error {
if err != nil {
return err
}
if err := stateFile.CheckTerraformVersion(); err != nil {
return err
if checkVersion {
if err := stateFile.CheckTerraformVersion(); err != nil {
return err
}
}

s.lineage = stateFile.Lineage
Expand Down Expand Up @@ -159,7 +167,7 @@ func (s *State) PersistState() error {
// We might be writing a new state altogether, but before we do that
// we'll check to make sure there isn't already a snapshot present
// that we ought to be updating.
err := s.refreshState()
err := s.refreshState(true)
if err != nil {
return fmt.Errorf("failed checking for existing remote state: %s", err)
}
Expand Down
15 changes: 10 additions & 5 deletions states/statemgr/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (s *Filesystem) WriteState(state *states.State) error {
defer s.mutex()()

if s.readFile == nil {
err := s.refreshState()
err := s.refreshState(true)
if err != nil {
return err
}
Expand Down Expand Up @@ -230,10 +230,15 @@ func (s *Filesystem) PersistState() error {
// RefreshState is an implementation of Refresher.
func (s *Filesystem) RefreshState() error {
defer s.mutex()()
return s.refreshState()
return s.refreshState(true)
}

func (s *Filesystem) refreshState() error {
func (s *Filesystem) RefreshStateWithoutCheckVersion() error {
defer s.mutex()()
return s.refreshState(false)
}

func (s *Filesystem) refreshState(checkVersion bool) error {
var reader io.Reader

// The s.readPath file is only OK to read if we have not written any state out
Expand Down Expand Up @@ -280,7 +285,7 @@ func (s *Filesystem) refreshState() error {
return err
}
log.Printf("[TRACE] statemgr.Filesystem: snapshot file has nil snapshot, but that's okay")
} else {
} else if checkVersion {
if err := f.CheckTerraformVersion(); err != nil {
return err
}
Expand Down Expand Up @@ -397,7 +402,7 @@ func (s *Filesystem) WriteStateForMigration(f *statefile.File, force bool) error
defer s.mutex()()

if s.readFile == nil {
err := s.refreshState()
err := s.refreshState(true)
if err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions states/statemgr/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func (s *LockDisabled) RefreshState() error {
return s.Inner.RefreshState()
}

func (s *LockDisabled) RefreshStateWithoutCheckVersion() error {
return s.Inner.RefreshStateWithoutCheckVersion()
}

func (s *LockDisabled) PersistState() error {
return s.Inner.PersistState()
}
Expand Down
10 changes: 9 additions & 1 deletion states/statemgr/persistent.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ type Persistent interface {
// PersistState that may be happening in other processes.
type Refresher interface {
// RefreshState retrieves a snapshot of state from persistent storage,
// returning an error if this is not possible.
// returning an error if this is not possible. If a snapshot is retrieved
// but is from an incompatible Terraform version, this will also result
// in an error.
//
// Types that implement RefreshState generally also implement a State
// method that returns the result of the latest successful refresh.
Expand All @@ -46,6 +48,12 @@ type Refresher interface {
// ephemeral portions of the state may be unpopulated after calling
// RefreshState.
RefreshState() error

// RefreshStateWithoutCheckVersion is similar to RefreshState, with the
// difference that it does not perform a Terraform version check of the
// state snapshot. Use with caution, as there is no guarantee that the
// state version retrieved is fully compatible.
RefreshStateWithoutCheckVersion() error
}

// Persister is the interface for managers that can write snapshots to
Expand Down
8 changes: 8 additions & 0 deletions states/statemgr/statemgr_fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func (m *fakeFull) RefreshState() error {
return m.t.WriteState(m.fakeP.State())
}

func (m *fakeFull) RefreshStateWithoutCheckVersion() error {
return m.t.WriteState(m.fakeP.State())
}

func (m *fakeFull) PersistState() error {
return m.fakeP.WriteState(m.t.State())
}
Expand Down Expand Up @@ -119,6 +123,10 @@ func (m *fakeErrorFull) RefreshState() error {
return errors.New("fake state manager error")
}

func (m *fakeErrorFull) RefreshStateWithoutCheckVersion() error {
return errors.New("fake state manager error")
}

func (m *fakeErrorFull) PersistState() error {
return errors.New("fake state manager error")
}
Expand Down

0 comments on commit 06af227

Please sign in to comment.