session.go 4.8 KB

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