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 }