github.com/sl1pm4t/terraform@v0.6.4-0.20170725213156-870617d22df3/state/remote/state.go (about) 1 package remote 2 3 import ( 4 "bytes" 5 "fmt" 6 "sync" 7 8 "github.com/hashicorp/terraform/state" 9 "github.com/hashicorp/terraform/terraform" 10 ) 11 12 // State implements the State interfaces in the state package to handle 13 // reading and writing the remote state. This State on its own does no 14 // local caching so every persist will go to the remote storage and local 15 // writes will go to memory. 16 type State struct { 17 mu sync.Mutex 18 19 Client Client 20 21 state, readState *terraform.State 22 } 23 24 // StateReader impl. 25 func (s *State) State() *terraform.State { 26 s.mu.Lock() 27 defer s.mu.Unlock() 28 29 return s.state.DeepCopy() 30 } 31 32 // StateWriter impl. 33 func (s *State) WriteState(state *terraform.State) error { 34 s.mu.Lock() 35 defer s.mu.Unlock() 36 37 if s.readState != nil && !state.SameLineage(s.readState) { 38 return fmt.Errorf("incompatible state lineage; given %s but want %s", state.Lineage, s.readState.Lineage) 39 } 40 41 // We create a deep copy of the state here, because the caller also has 42 // a reference to the given object and can potentially go on to mutate 43 // it after we return, but we want the snapshot at this point in time. 44 s.state = state.DeepCopy() 45 46 // Force our new state to have the same serial as our read state. We'll 47 // update this if PersistState is called later. (We don't require nor trust 48 // the caller to properly maintain serial for transient state objects since 49 // the rest of Terraform treats state as an openly mutable object.) 50 // 51 // If we have no read state then we assume we're either writing a new 52 // state for the first time or we're migrating a state from elsewhere, 53 // and in both cases we wish to retain the lineage and serial from 54 // the given state. 55 if s.readState != nil { 56 s.state.Serial = s.readState.Serial 57 } 58 59 return nil 60 } 61 62 // StateRefresher impl. 63 func (s *State) RefreshState() error { 64 s.mu.Lock() 65 defer s.mu.Unlock() 66 67 payload, err := s.Client.Get() 68 if err != nil { 69 return err 70 } 71 72 // no remote state is OK 73 if payload == nil { 74 return nil 75 } 76 77 state, err := terraform.ReadState(bytes.NewReader(payload.Data)) 78 if err != nil { 79 return err 80 } 81 82 s.state = state 83 s.readState = s.state.DeepCopy() // our states must be separate instances so we can track changes 84 return nil 85 } 86 87 // StatePersister impl. 88 func (s *State) PersistState() error { 89 s.mu.Lock() 90 defer s.mu.Unlock() 91 92 if !s.state.MarshalEqual(s.readState) { 93 // Our new state does not marshal as byte-for-byte identical to 94 // the old, so we need to increment the serial. 95 // Note that in WriteState we force the serial to match that of 96 // s.readState, if we have a readState. 97 s.state.Serial++ 98 } 99 100 var buf bytes.Buffer 101 if err := terraform.WriteState(s.state, &buf); err != nil { 102 return err 103 } 104 105 err := s.Client.Put(buf.Bytes()) 106 if err != nil { 107 return err 108 } 109 110 // After we've successfully persisted, what we just wrote is our new 111 // reference state until someone calls RefreshState again. 112 s.readState = s.state.DeepCopy() 113 return nil 114 } 115 116 // Lock calls the Client's Lock method if it's implemented. 117 func (s *State) Lock(info *state.LockInfo) (string, error) { 118 s.mu.Lock() 119 defer s.mu.Unlock() 120 121 if c, ok := s.Client.(ClientLocker); ok { 122 return c.Lock(info) 123 } 124 return "", nil 125 } 126 127 // Unlock calls the Client's Unlock method if it's implemented. 128 func (s *State) Unlock(id string) error { 129 s.mu.Lock() 130 defer s.mu.Unlock() 131 132 if c, ok := s.Client.(ClientLocker); ok { 133 return c.Unlock(id) 134 } 135 return nil 136 }