github.com/kevinklinger/open_terraform@v1.3.6/noninternal/states/remote/state.go (about)

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sync"
     7  
     8  	uuid "github.com/hashicorp/go-uuid"
     9  
    10  	"github.com/kevinklinger/open_terraform/noninternal/states"
    11  	"github.com/kevinklinger/open_terraform/noninternal/states/statefile"
    12  	"github.com/kevinklinger/open_terraform/noninternal/states/statemgr"
    13  	"github.com/kevinklinger/open_terraform/noninternal/terraform"
    14  )
    15  
    16  // State implements the State interfaces in the state package to handle
    17  // reading and writing the remote state. This State on its own does no
    18  // local caching so every persist will go to the remote storage and local
    19  // writes will go to memory.
    20  type State struct {
    21  	mu sync.Mutex
    22  
    23  	Client Client
    24  
    25  	// We track two pieces of meta data in addition to the state itself:
    26  	//
    27  	// lineage - the state's unique ID
    28  	// serial  - the monotonic counter of "versions" of the state
    29  	//
    30  	// Both of these (along with state) have a sister field
    31  	// that represents the values read in from an existing source.
    32  	// All three of these values are used to determine if the new
    33  	// state has changed from an existing state we read in.
    34  	lineage, readLineage string
    35  	serial, readSerial   uint64
    36  	state, readState     *states.State
    37  	disableLocks         bool
    38  }
    39  
    40  var _ statemgr.Full = (*State)(nil)
    41  var _ statemgr.Migrator = (*State)(nil)
    42  
    43  // statemgr.Reader impl.
    44  func (s *State) State() *states.State {
    45  	s.mu.Lock()
    46  	defer s.mu.Unlock()
    47  
    48  	return s.state.DeepCopy()
    49  }
    50  
    51  func (s *State) GetRootOutputValues() (map[string]*states.OutputValue, error) {
    52  	if err := s.RefreshState(); err != nil {
    53  		return nil, fmt.Errorf("Failed to load state: %s", err)
    54  	}
    55  
    56  	state := s.State()
    57  	if state == nil {
    58  		state = states.NewState()
    59  	}
    60  
    61  	return state.RootModule().OutputValues, nil
    62  }
    63  
    64  // StateForMigration is part of our implementation of statemgr.Migrator.
    65  func (s *State) StateForMigration() *statefile.File {
    66  	s.mu.Lock()
    67  	defer s.mu.Unlock()
    68  
    69  	return statefile.New(s.state.DeepCopy(), s.lineage, s.serial)
    70  }
    71  
    72  // statemgr.Writer impl.
    73  func (s *State) WriteState(state *states.State) error {
    74  	s.mu.Lock()
    75  	defer s.mu.Unlock()
    76  
    77  	// We create a deep copy of the state here, because the caller also has
    78  	// a reference to the given object and can potentially go on to mutate
    79  	// it after we return, but we want the snapshot at this point in time.
    80  	s.state = state.DeepCopy()
    81  
    82  	return nil
    83  }
    84  
    85  // WriteStateForMigration is part of our implementation of statemgr.Migrator.
    86  func (s *State) WriteStateForMigration(f *statefile.File, force bool) error {
    87  	s.mu.Lock()
    88  	defer s.mu.Unlock()
    89  
    90  	if !force {
    91  		checkFile := statefile.New(s.state, s.lineage, s.serial)
    92  		if err := statemgr.CheckValidImport(f, checkFile); err != nil {
    93  			return err
    94  		}
    95  	}
    96  
    97  	// The remote backend needs to pass the `force` flag through to its client.
    98  	// For backends that support such operations, inform the client
    99  	// that a force push has been requested
   100  	c, isForcePusher := s.Client.(ClientForcePusher)
   101  	if force && isForcePusher {
   102  		c.EnableForcePush()
   103  	}
   104  
   105  	// We create a deep copy of the state here, because the caller also has
   106  	// a reference to the given object and can potentially go on to mutate
   107  	// it after we return, but we want the snapshot at this point in time.
   108  	s.state = f.State.DeepCopy()
   109  	s.lineage = f.Lineage
   110  	s.serial = f.Serial
   111  
   112  	return nil
   113  }
   114  
   115  // statemgr.Refresher impl.
   116  func (s *State) RefreshState() error {
   117  	s.mu.Lock()
   118  	defer s.mu.Unlock()
   119  	return s.refreshState()
   120  }
   121  
   122  // refreshState is the main implementation of RefreshState, but split out so
   123  // that we can make internal calls to it from methods that are already holding
   124  // the s.mu lock.
   125  func (s *State) refreshState() error {
   126  	payload, err := s.Client.Get()
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	// no remote state is OK
   132  	if payload == nil {
   133  		s.readState = nil
   134  		s.lineage = ""
   135  		s.serial = 0
   136  		return nil
   137  	}
   138  
   139  	stateFile, err := statefile.Read(bytes.NewReader(payload.Data))
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	s.lineage = stateFile.Lineage
   145  	s.serial = stateFile.Serial
   146  	s.state = stateFile.State
   147  
   148  	// Properties from the remote must be separate so we can
   149  	// track changes as lineage, serial and/or state are mutated
   150  	s.readLineage = stateFile.Lineage
   151  	s.readSerial = stateFile.Serial
   152  	s.readState = s.state.DeepCopy()
   153  	return nil
   154  }
   155  
   156  // statemgr.Persister impl.
   157  func (s *State) PersistState(schemas *terraform.Schemas) error {
   158  	s.mu.Lock()
   159  	defer s.mu.Unlock()
   160  
   161  	if s.readState != nil {
   162  		lineageUnchanged := s.readLineage != "" && s.lineage == s.readLineage
   163  		serialUnchanged := s.readSerial != 0 && s.serial == s.readSerial
   164  		stateUnchanged := statefile.StatesMarshalEqual(s.state, s.readState)
   165  		if stateUnchanged && lineageUnchanged && serialUnchanged {
   166  			// If the state, lineage or serial haven't changed at all then we have nothing to do.
   167  			return nil
   168  		}
   169  		s.serial++
   170  	} else {
   171  		// We might be writing a new state altogether, but before we do that
   172  		// we'll check to make sure there isn't already a snapshot present
   173  		// that we ought to be updating.
   174  		err := s.refreshState()
   175  		if err != nil {
   176  			return fmt.Errorf("failed checking for existing remote state: %s", err)
   177  		}
   178  		if s.lineage == "" { // indicates that no state snapshot is present yet
   179  			lineage, err := uuid.GenerateUUID()
   180  			if err != nil {
   181  				return fmt.Errorf("failed to generate initial lineage: %v", err)
   182  			}
   183  			s.lineage = lineage
   184  			s.serial = 0
   185  		}
   186  	}
   187  
   188  	f := statefile.New(s.state, s.lineage, s.serial)
   189  
   190  	var buf bytes.Buffer
   191  	err := statefile.Write(f, &buf)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	err = s.Client.Put(buf.Bytes())
   197  	if err != nil {
   198  		return err
   199  	}
   200  
   201  	// After we've successfully persisted, what we just wrote is our new
   202  	// reference state until someone calls RefreshState again.
   203  	// We've potentially overwritten (via force) the state, lineage
   204  	// and / or serial (and serial was incremented) so we copy over all
   205  	// three fields so everything matches the new state and a subsequent
   206  	// operation would correctly detect no changes to the lineage, serial or state.
   207  	s.readState = s.state.DeepCopy()
   208  	s.readLineage = s.lineage
   209  	s.readSerial = s.serial
   210  	return nil
   211  }
   212  
   213  // Lock calls the Client's Lock method if it's implemented.
   214  func (s *State) Lock(info *statemgr.LockInfo) (string, error) {
   215  	s.mu.Lock()
   216  	defer s.mu.Unlock()
   217  
   218  	if s.disableLocks {
   219  		return "", nil
   220  	}
   221  
   222  	if c, ok := s.Client.(ClientLocker); ok {
   223  		return c.Lock(info)
   224  	}
   225  	return "", nil
   226  }
   227  
   228  // Unlock calls the Client's Unlock method if it's implemented.
   229  func (s *State) Unlock(id string) error {
   230  	s.mu.Lock()
   231  	defer s.mu.Unlock()
   232  
   233  	if s.disableLocks {
   234  		return nil
   235  	}
   236  
   237  	if c, ok := s.Client.(ClientLocker); ok {
   238  		return c.Unlock(id)
   239  	}
   240  	return nil
   241  }
   242  
   243  // DisableLocks turns the Lock and Unlock methods into no-ops. This is intended
   244  // to be called during initialization of a state manager and should not be
   245  // called after any of the statemgr.Full interface methods have been called.
   246  func (s *State) DisableLocks() {
   247  	s.disableLocks = true
   248  }
   249  
   250  // StateSnapshotMeta returns the metadata from the most recently persisted
   251  // or refreshed persistent state snapshot.
   252  //
   253  // This is an implementation of statemgr.PersistentMeta.
   254  func (s *State) StateSnapshotMeta() statemgr.SnapshotMeta {
   255  	return statemgr.SnapshotMeta{
   256  		Lineage: s.lineage,
   257  		Serial:  s.serial,
   258  	}
   259  }