github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/state/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/hashicorp/terraform/state" 10 "github.com/hashicorp/terraform/states" 11 "github.com/hashicorp/terraform/states/statefile" 12 "github.com/hashicorp/terraform/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 // StateForMigration is part of our implementation of statemgr.Migrator. 51 func (s *State) StateForMigration() *statefile.File { 52 s.mu.Lock() 53 defer s.mu.Unlock() 54 55 return statefile.New(s.state.DeepCopy(), s.lineage, s.serial) 56 } 57 58 // statemgr.Writer impl. 59 func (s *State) WriteState(state *states.State) error { 60 s.mu.Lock() 61 defer s.mu.Unlock() 62 63 // We create a deep copy of the state here, because the caller also has 64 // a reference to the given object and can potentially go on to mutate 65 // it after we return, but we want the snapshot at this point in time. 66 s.state = state.DeepCopy() 67 68 return nil 69 } 70 71 // WriteStateForMigration is part of our implementation of statemgr.Migrator. 72 func (s *State) WriteStateForMigration(f *statefile.File, force bool) error { 73 s.mu.Lock() 74 defer s.mu.Unlock() 75 76 // `force` is passed down from the CLI flag and terminates here. Actual 77 // force pushing with the remote backend happens when Put()'ing the contents 78 // in the backend. If force is specified we skip verifications and hand the 79 // context off to the client to use when persitence operations actually take place. 80 c, isForcePusher := s.Client.(ClientForcePusher) 81 if force && isForcePusher { 82 c.EnableForcePush() 83 } else { 84 checkFile := statefile.New(s.state, s.lineage, s.serial) 85 if err := statemgr.CheckValidImport(f, checkFile); err != nil { 86 return err 87 } 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(true) 105 } 106 107 func (s *State) RefreshStateWithoutCheckVersion() error { 108 s.mu.Lock() 109 defer s.mu.Unlock() 110 return s.refreshState(false) 111 } 112 113 // refreshState is the main implementation of RefreshState, but split out so 114 // that we can make internal calls to it from methods that are already holding 115 // the s.mu lock. 116 func (s *State) refreshState(checkVersion bool) error { 117 payload, err := s.Client.Get() 118 if err != nil { 119 return err 120 } 121 122 // no remote state is OK 123 if payload == nil { 124 s.readState = nil 125 s.lineage = "" 126 s.serial = 0 127 return nil 128 } 129 130 stateFile, err := statefile.Read(bytes.NewReader(payload.Data)) 131 if err != nil { 132 return err 133 } 134 if checkVersion { 135 if err := stateFile.CheckTerraformVersion(); err != nil { 136 return err 137 } 138 } 139 140 s.lineage = stateFile.Lineage 141 s.serial = stateFile.Serial 142 s.state = stateFile.State 143 144 // Properties from the remote must be separate so we can 145 // track changes as lineage, serial and/or state are mutated 146 s.readLineage = stateFile.Lineage 147 s.readSerial = stateFile.Serial 148 s.readState = s.state.DeepCopy() 149 return nil 150 } 151 152 // statemgr.Persister impl. 153 func (s *State) PersistState() error { 154 s.mu.Lock() 155 defer s.mu.Unlock() 156 157 if s.readState != nil { 158 lineageUnchanged := s.readLineage != "" && s.lineage == s.readLineage 159 serialUnchanged := s.readSerial != 0 && s.serial == s.readSerial 160 stateUnchanged := statefile.StatesMarshalEqual(s.state, s.readState) 161 if stateUnchanged && lineageUnchanged && serialUnchanged { 162 // If the state, lineage or serial haven't changed at all then we have nothing to do. 163 return nil 164 } 165 s.serial++ 166 } else { 167 // We might be writing a new state altogether, but before we do that 168 // we'll check to make sure there isn't already a snapshot present 169 // that we ought to be updating. 170 err := s.refreshState(true) 171 if err != nil { 172 return fmt.Errorf("failed checking for existing remote state: %s", err) 173 } 174 if s.lineage == "" { // indicates that no state snapshot is present yet 175 lineage, err := uuid.GenerateUUID() 176 if err != nil { 177 return fmt.Errorf("failed to generate initial lineage: %v", err) 178 } 179 s.lineage = lineage 180 s.serial = 0 181 } 182 } 183 184 f := statefile.New(s.state, s.lineage, s.serial) 185 186 var buf bytes.Buffer 187 err := statefile.Write(f, &buf) 188 if err != nil { 189 return err 190 } 191 192 err = s.Client.Put(buf.Bytes()) 193 if err != nil { 194 return err 195 } 196 197 // After we've successfully persisted, what we just wrote is our new 198 // reference state until someone calls RefreshState again. 199 // We've potentially overwritten (via force) the state, lineage 200 // and / or serial (and serial was incremented) so we copy over all 201 // three fields so everything matches the new state and a subsequent 202 // operation would correctly detect no changes to the lineage, serial or state. 203 s.readState = s.state.DeepCopy() 204 s.readLineage = s.lineage 205 s.readSerial = s.serial 206 return nil 207 } 208 209 // Lock calls the Client's Lock method if it's implemented. 210 func (s *State) Lock(info *state.LockInfo) (string, error) { 211 s.mu.Lock() 212 defer s.mu.Unlock() 213 214 if s.disableLocks { 215 return "", nil 216 } 217 218 if c, ok := s.Client.(ClientLocker); ok { 219 return c.Lock(info) 220 } 221 return "", nil 222 } 223 224 // Unlock calls the Client's Unlock method if it's implemented. 225 func (s *State) Unlock(id string) error { 226 s.mu.Lock() 227 defer s.mu.Unlock() 228 229 if s.disableLocks { 230 return nil 231 } 232 233 if c, ok := s.Client.(ClientLocker); ok { 234 return c.Unlock(id) 235 } 236 return nil 237 } 238 239 // DisableLocks turns the Lock and Unlock methods into no-ops. This is intended 240 // to be called during initialization of a state manager and should not be 241 // called after any of the statemgr.Full interface methods have been called. 242 func (s *State) DisableLocks() { 243 s.disableLocks = true 244 } 245 246 // StateSnapshotMeta returns the metadata from the most recently persisted 247 // or refreshed persistent state snapshot. 248 // 249 // This is an implementation of statemgr.PersistentMeta. 250 func (s *State) StateSnapshotMeta() statemgr.SnapshotMeta { 251 return statemgr.SnapshotMeta{ 252 Lineage: s.lineage, 253 Serial: s.serial, 254 } 255 }