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