github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/state/backup.go (about)

     1  package state
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/hashicorp/terraform/states"
     7  	"github.com/hashicorp/terraform/states/statemgr"
     8  )
     9  
    10  // BackupState wraps a State that backs up the state on the first time that
    11  // a WriteState or PersistState is called.
    12  //
    13  // If Path exists, it will be overwritten.
    14  type BackupState struct {
    15  	mu   sync.Mutex
    16  	Real State
    17  	Path string
    18  
    19  	done bool
    20  }
    21  
    22  func (s *BackupState) State() *states.State {
    23  	return s.Real.State()
    24  }
    25  
    26  func (s *BackupState) RefreshState() error {
    27  	return s.Real.RefreshState()
    28  }
    29  
    30  func (s *BackupState) WriteState(state *states.State) error {
    31  	s.mu.Lock()
    32  	defer s.mu.Unlock()
    33  
    34  	if !s.done {
    35  		if err := s.backup(); err != nil {
    36  			return err
    37  		}
    38  	}
    39  
    40  	return s.Real.WriteState(state)
    41  }
    42  
    43  func (s *BackupState) PersistState() error {
    44  	s.mu.Lock()
    45  	defer s.mu.Unlock()
    46  
    47  	if !s.done {
    48  		if err := s.backup(); err != nil {
    49  			return err
    50  		}
    51  	}
    52  
    53  	return s.Real.PersistState()
    54  }
    55  
    56  func (s *BackupState) Lock(info *LockInfo) (string, error) {
    57  	return s.Real.Lock(info)
    58  }
    59  
    60  func (s *BackupState) Unlock(id string) error {
    61  	return s.Real.Unlock(id)
    62  }
    63  
    64  func (s *BackupState) backup() error {
    65  	state := s.Real.State()
    66  	if state == nil {
    67  		if err := s.Real.RefreshState(); err != nil {
    68  			return err
    69  		}
    70  
    71  		state = s.Real.State()
    72  	}
    73  
    74  	// LocalState.WriteState ensures that a file always exists for locking
    75  	// purposes, but we don't need a backup or lock if the state is empty, so
    76  	// skip this with a nil state.
    77  	if state != nil {
    78  		ls := statemgr.NewFilesystem(s.Path)
    79  		if err := ls.WriteState(state); err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	s.done = true
    85  	return nil
    86  }