github.com/r3vit/terraform@v0.11.9-beta1.0.20181016131357-87d05607d3c5/state/remote/state.go (about)

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"log"
     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  		// This can't error here, because we need to be able to overwrite the
    39  		// state in some cases, like `state push -force` or `workspace new
    40  		// -state=`
    41  		log.Printf("[WARN] incompatible state lineage; given %s but want %s", state.Lineage, s.readState.Lineage)
    42  	}
    43  
    44  	// We create a deep copy of the state here, because the caller also has
    45  	// a reference to the given object and can potentially go on to mutate
    46  	// it after we return, but we want the snapshot at this point in time.
    47  	s.state = state.DeepCopy()
    48  
    49  	// Force our new state to have the same serial as our read state. We'll
    50  	// update this if PersistState is called later. (We don't require nor trust
    51  	// the caller to properly maintain serial for transient state objects since
    52  	// the rest of Terraform treats state as an openly mutable object.)
    53  	//
    54  	// If we have no read state then we assume we're either writing a new
    55  	// state for the first time or we're migrating a state from elsewhere,
    56  	// and in both cases we wish to retain the lineage and serial from
    57  	// the given state.
    58  	if s.readState != nil {
    59  		s.state.Serial = s.readState.Serial
    60  	}
    61  
    62  	return nil
    63  }
    64  
    65  // StateRefresher impl.
    66  func (s *State) RefreshState() error {
    67  	s.mu.Lock()
    68  	defer s.mu.Unlock()
    69  
    70  	payload, err := s.Client.Get()
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	// no remote state is OK
    76  	if payload == nil {
    77  		return nil
    78  	}
    79  
    80  	state, err := terraform.ReadState(bytes.NewReader(payload.Data))
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	s.state = state
    86  	s.readState = s.state.DeepCopy() // our states must be separate instances so we can track changes
    87  	return nil
    88  }
    89  
    90  // StatePersister impl.
    91  func (s *State) PersistState() error {
    92  	s.mu.Lock()
    93  	defer s.mu.Unlock()
    94  
    95  	if !s.state.MarshalEqual(s.readState) {
    96  		// Our new state does not marshal as byte-for-byte identical to
    97  		// the old, so we need to increment the serial.
    98  		// Note that in WriteState we force the serial to match that of
    99  		// s.readState, if we have a readState.
   100  		s.state.Serial++
   101  	}
   102  
   103  	var buf bytes.Buffer
   104  	if err := terraform.WriteState(s.state, &buf); err != nil {
   105  		return err
   106  	}
   107  
   108  	err := s.Client.Put(buf.Bytes())
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	// After we've successfully persisted, what we just wrote is our new
   114  	// reference state until someone calls RefreshState again.
   115  	s.readState = s.state.DeepCopy()
   116  	return nil
   117  }
   118  
   119  // Lock calls the Client's Lock method if it's implemented.
   120  func (s *State) Lock(info *state.LockInfo) (string, error) {
   121  	s.mu.Lock()
   122  	defer s.mu.Unlock()
   123  
   124  	if c, ok := s.Client.(ClientLocker); ok {
   125  		return c.Lock(info)
   126  	}
   127  	return "", nil
   128  }
   129  
   130  // Unlock calls the Client's Unlock method if it's implemented.
   131  func (s *State) Unlock(id string) error {
   132  	s.mu.Lock()
   133  	defer s.mu.Unlock()
   134  
   135  	if c, ok := s.Client.(ClientLocker); ok {
   136  		return c.Unlock(id)
   137  	}
   138  	return nil
   139  }