1package api 2 3import ( 4 "fmt" 5 "time" 6) 7 8const ( 9 // SessionBehaviorRelease is the default behavior and causes 10 // all associated locks to be released on session invalidation. 11 SessionBehaviorRelease = "release" 12 13 // SessionBehaviorDelete is new in Consul 0.5 and changes the 14 // behavior to delete all associated locks on session invalidation. 15 // It can be used in a way similar to Ephemeral Nodes in ZooKeeper. 16 SessionBehaviorDelete = "delete" 17) 18 19// SessionEntry represents a session in consul 20type SessionEntry struct { 21 CreateIndex uint64 22 ID string 23 Name string 24 Node string 25 Checks []string 26 LockDelay time.Duration 27 Behavior string 28 TTL string 29} 30 31// Session can be used to query the Session endpoints 32type Session struct { 33 c *Client 34} 35 36// Session returns a handle to the session endpoints 37func (c *Client) Session() *Session { 38 return &Session{c} 39} 40 41// CreateNoChecks is like Create but is used specifically to create 42// a session with no associated health checks. 43func (s *Session) CreateNoChecks(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) { 44 body := make(map[string]interface{}) 45 body["Checks"] = []string{} 46 if se != nil { 47 if se.Name != "" { 48 body["Name"] = se.Name 49 } 50 if se.Node != "" { 51 body["Node"] = se.Node 52 } 53 if se.LockDelay != 0 { 54 body["LockDelay"] = durToMsec(se.LockDelay) 55 } 56 if se.Behavior != "" { 57 body["Behavior"] = se.Behavior 58 } 59 if se.TTL != "" { 60 body["TTL"] = se.TTL 61 } 62 } 63 return s.create(body, q) 64 65} 66 67// Create makes a new session. Providing a session entry can 68// customize the session. It can also be nil to use defaults. 69func (s *Session) Create(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) { 70 var obj interface{} 71 if se != nil { 72 body := make(map[string]interface{}) 73 obj = body 74 if se.Name != "" { 75 body["Name"] = se.Name 76 } 77 if se.Node != "" { 78 body["Node"] = se.Node 79 } 80 if se.LockDelay != 0 { 81 body["LockDelay"] = durToMsec(se.LockDelay) 82 } 83 if len(se.Checks) > 0 { 84 body["Checks"] = se.Checks 85 } 86 if se.Behavior != "" { 87 body["Behavior"] = se.Behavior 88 } 89 if se.TTL != "" { 90 body["TTL"] = se.TTL 91 } 92 } 93 return s.create(obj, q) 94} 95 96func (s *Session) create(obj interface{}, q *WriteOptions) (string, *WriteMeta, error) { 97 var out struct{ ID string } 98 wm, err := s.c.write("/v1/session/create", obj, &out, q) 99 if err != nil { 100 return "", nil, err 101 } 102 return out.ID, wm, nil 103} 104 105// Destroy invalides a given session 106func (s *Session) Destroy(id string, q *WriteOptions) (*WriteMeta, error) { 107 wm, err := s.c.write("/v1/session/destroy/"+id, nil, nil, q) 108 if err != nil { 109 return nil, err 110 } 111 return wm, nil 112} 113 114// Renew renews the TTL on a given session 115func (s *Session) Renew(id string, q *WriteOptions) (*SessionEntry, *WriteMeta, error) { 116 var entries []*SessionEntry 117 wm, err := s.c.write("/v1/session/renew/"+id, nil, &entries, q) 118 if err != nil { 119 return nil, nil, err 120 } 121 if len(entries) > 0 { 122 return entries[0], wm, nil 123 } 124 return nil, wm, nil 125} 126 127// RenewPeriodic is used to periodically invoke Session.Renew on a 128// session until a doneCh is closed. This is meant to be used in a long running 129// goroutine to ensure a session stays valid. 130func (s *Session) RenewPeriodic(initialTTL string, id string, q *WriteOptions, doneCh chan struct{}) error { 131 ttl, err := time.ParseDuration(initialTTL) 132 if err != nil { 133 return err 134 } 135 136 waitDur := ttl / 2 137 lastRenewTime := time.Now() 138 var lastErr error 139 for { 140 if time.Since(lastRenewTime) > ttl { 141 return lastErr 142 } 143 select { 144 case <-time.After(waitDur): 145 entry, _, err := s.Renew(id, q) 146 if err != nil { 147 waitDur = time.Second 148 lastErr = err 149 continue 150 } 151 if entry == nil { 152 waitDur = time.Second 153 lastErr = fmt.Errorf("No SessionEntry returned") 154 continue 155 } 156 157 // Handle the server updating the TTL 158 ttl, _ = time.ParseDuration(entry.TTL) 159 waitDur = ttl / 2 160 lastRenewTime = time.Now() 161 162 case <-doneCh: 163 // Attempt a session destroy 164 s.Destroy(id, q) 165 return nil 166 } 167 } 168} 169 170// Info looks up a single session 171func (s *Session) Info(id string, q *QueryOptions) (*SessionEntry, *QueryMeta, error) { 172 var entries []*SessionEntry 173 qm, err := s.c.query("/v1/session/info/"+id, &entries, q) 174 if err != nil { 175 return nil, nil, err 176 } 177 if len(entries) > 0 { 178 return entries[0], qm, nil 179 } 180 return nil, qm, nil 181} 182 183// List gets sessions for a node 184func (s *Session) Node(node string, q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) { 185 var entries []*SessionEntry 186 qm, err := s.c.query("/v1/session/node/"+node, &entries, q) 187 if err != nil { 188 return nil, nil, err 189 } 190 return entries, qm, nil 191} 192 193// List gets all active sessions 194func (s *Session) List(q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) { 195 var entries []*SessionEntry 196 qm, err := s.c.query("/v1/session/list", &entries, q) 197 if err != nil { 198 return nil, nil, err 199 } 200 return entries, qm, nil 201} 202