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 }