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