github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/state/backup.go (about)

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