github.com/sl1pm4t/terraform@v0.6.4-0.20170725213156-870617d22df3/state/remote/state.go (about)

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/hashicorp/terraform/state"
     9  	"github.com/hashicorp/terraform/terraform"
    10  )
    11  
    12  // State implements the State interfaces in the state package to handle
    13  // reading and writing the remote state. This State on its own does no
    14  // local caching so every persist will go to the remote storage and local
    15  // writes will go to memory.
    16  type State struct {
    17  	mu sync.Mutex
    18  
    19  	Client Client
    20  
    21  	state, readState *terraform.State
    22  }
    23  
    24  // StateReader impl.
    25  func (s *State) State() *terraform.State {
    26  	s.mu.Lock()
    27  	defer s.mu.Unlock()
    28  
    29  	return s.state.DeepCopy()
    30  }
    31  
    32  // StateWriter impl.
    33  func (s *State) WriteState(state *terraform.State) error {
    34  	s.mu.Lock()
    35  	defer s.mu.Unlock()
    36  
    37  	if s.readState != nil && !state.SameLineage(s.readState) {
    38  		return fmt.Errorf("incompatible state lineage; given %s but want %s", state.Lineage, s.readState.Lineage)
    39  	}
    40  
    41  	// We create a deep copy of the state here, because the caller also has
    42  	// a reference to the given object and can potentially go on to mutate
    43  	// it after we return, but we want the snapshot at this point in time.
    44  	s.state = state.DeepCopy()
    45  
    46  	// Force our new state to have the same serial as our read state. We'll
    47  	// update this if PersistState is called later. (We don't require nor trust
    48  	// the caller to properly maintain serial for transient state objects since
    49  	// the rest of Terraform treats state as an openly mutable object.)
    50  	//
    51  	// If we have no read state then we assume we're either writing a new
    52  	// state for the first time or we're migrating a state from elsewhere,
    53  	// and in both cases we wish to retain the lineage and serial from
    54  	// the given state.
    55  	if s.readState != nil {
    56  		s.state.Serial = s.readState.Serial
    57  	}
    58  
    59  	return nil
    60  }
    61  
    62  // StateRefresher impl.
    63  func (s *State) RefreshState() error {
    64  	s.mu.Lock()
    65  	defer s.mu.Unlock()
    66  
    67  	payload, err := s.Client.Get()
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	// no remote state is OK
    73  	if payload == nil {
    74  		return nil
    75  	}
    76  
    77  	state, err := terraform.ReadState(bytes.NewReader(payload.Data))
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	s.state = state
    83  	s.readState = s.state.DeepCopy() // our states must be separate instances so we can track changes
    84  	return nil
    85  }
    86  
    87  // StatePersister impl.
    88  func (s *State) PersistState() error {
    89  	s.mu.Lock()
    90  	defer s.mu.Unlock()
    91  
    92  	if !s.state.MarshalEqual(s.readState) {
    93  		// Our new state does not marshal as byte-for-byte identical to
    94  		// the old, so we need to increment the serial.
    95  		// Note that in WriteState we force the serial to match that of
    96  		// s.readState, if we have a readState.
    97  		s.state.Serial++
    98  	}
    99  
   100  	var buf bytes.Buffer
   101  	if err := terraform.WriteState(s.state, &buf); err != nil {
   102  		return err
   103  	}
   104  
   105  	err := s.Client.Put(buf.Bytes())
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	// After we've successfully persisted, what we just wrote is our new
   111  	// reference state until someone calls RefreshState again.
   112  	s.readState = s.state.DeepCopy()
   113  	return nil
   114  }
   115  
   116  // Lock calls the Client's Lock method if it's implemented.
   117  func (s *State) Lock(info *state.LockInfo) (string, error) {
   118  	s.mu.Lock()
   119  	defer s.mu.Unlock()
   120  
   121  	if c, ok := s.Client.(ClientLocker); ok {
   122  		return c.Lock(info)
   123  	}
   124  	return "", nil
   125  }
   126  
   127  // Unlock calls the Client's Unlock method if it's implemented.
   128  func (s *State) Unlock(id string) error {
   129  	s.mu.Lock()
   130  	defer s.mu.Unlock()
   131  
   132  	if c, ok := s.Client.(ClientLocker); ok {
   133  		return c.Unlock(id)
   134  	}
   135  	return nil
   136  }