PageRenderTime 254ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/github.com/hashicorp/consul/command/agent/user_event.go

https://github.com/backstage/backstage
Go | 265 lines | 191 code | 34 blank | 40 comment | 62 complexity | 3bf7730a7d96d5f771a6df37ae801c8d MD5 | raw file
Possible License(s): Apache-2.0, MIT, BSD-3-Clause, MPL-2.0-no-copyleft-exception
  1. package agent
  2. import (
  3. "fmt"
  4. "regexp"
  5. "github.com/hashicorp/consul/consul/structs"
  6. "github.com/hashicorp/go-uuid"
  7. )
  8. const (
  9. // userEventMaxVersion is the maximum protocol version we understand
  10. userEventMaxVersion = 1
  11. // remoteExecName is the event name for a remote exec command
  12. remoteExecName = "_rexec"
  13. )
  14. // UserEventParam is used to parameterize a user event
  15. type UserEvent struct {
  16. // ID of the user event. Automatically generated.
  17. ID string
  18. // Name of the event
  19. Name string `codec:"n"`
  20. // Optional payload
  21. Payload []byte `codec:"p,omitempty"`
  22. // NodeFilter is a regular expression to filter on nodes
  23. NodeFilter string `codec:"nf,omitempty"`
  24. // ServiceFilter is a regular expression to filter on services
  25. ServiceFilter string `codec:"sf,omitempty"`
  26. // TagFilter is a regular expression to filter on tags of a service,
  27. // must be provided with ServiceFilter
  28. TagFilter string `codec:"tf,omitempty"`
  29. // Version of the user event. Automatically generated.
  30. Version int `codec:"v"`
  31. // LTime is the lamport time. Automatically generated.
  32. LTime uint64 `codec:"-"`
  33. }
  34. // validateUserEventParams is used to sanity check the inputs
  35. func validateUserEventParams(params *UserEvent) error {
  36. // Validate the inputs
  37. if params.Name == "" {
  38. return fmt.Errorf("User event missing name")
  39. }
  40. if params.TagFilter != "" && params.ServiceFilter == "" {
  41. return fmt.Errorf("Cannot provide tag filter without service filter")
  42. }
  43. if params.NodeFilter != "" {
  44. if _, err := regexp.Compile(params.NodeFilter); err != nil {
  45. return fmt.Errorf("Invalid node filter: %v", err)
  46. }
  47. }
  48. if params.ServiceFilter != "" {
  49. if _, err := regexp.Compile(params.ServiceFilter); err != nil {
  50. return fmt.Errorf("Invalid service filter: %v", err)
  51. }
  52. }
  53. if params.TagFilter != "" {
  54. if _, err := regexp.Compile(params.TagFilter); err != nil {
  55. return fmt.Errorf("Invalid tag filter: %v", err)
  56. }
  57. }
  58. return nil
  59. }
  60. // UserEvent is used to fire an event via the Serf layer on the LAN
  61. func (a *Agent) UserEvent(dc, token string, params *UserEvent) error {
  62. // Validate the params
  63. if err := validateUserEventParams(params); err != nil {
  64. return err
  65. }
  66. // Format message
  67. var err error
  68. if params.ID, err = uuid.GenerateUUID(); err != nil {
  69. return fmt.Errorf("UUID generation failed: %v", err)
  70. }
  71. params.Version = userEventMaxVersion
  72. payload, err := encodeMsgPack(&params)
  73. if err != nil {
  74. return fmt.Errorf("UserEvent encoding failed: %v", err)
  75. }
  76. // Service the event fire over RPC. This ensures that we authorize
  77. // the request against the token first.
  78. args := structs.EventFireRequest{
  79. Datacenter: dc,
  80. Name: params.Name,
  81. Payload: payload,
  82. QueryOptions: structs.QueryOptions{Token: token},
  83. }
  84. // Any server can process in the remote DC, since the
  85. // gossip will take over anyways
  86. args.AllowStale = true
  87. var out structs.EventFireResponse
  88. return a.RPC("Internal.EventFire", &args, &out)
  89. }
  90. // handleEvents is used to process incoming user events
  91. func (a *Agent) handleEvents() {
  92. for {
  93. select {
  94. case e := <-a.eventCh:
  95. // Decode the event
  96. msg := new(UserEvent)
  97. if err := decodeMsgPack(e.Payload, msg); err != nil {
  98. a.logger.Printf("[ERR] agent: Failed to decode event: %v", err)
  99. continue
  100. }
  101. msg.LTime = uint64(e.LTime)
  102. // Skip if we don't pass filtering
  103. if !a.shouldProcessUserEvent(msg) {
  104. continue
  105. }
  106. // Ingest the event
  107. a.ingestUserEvent(msg)
  108. case <-a.shutdownCh:
  109. return
  110. }
  111. }
  112. }
  113. // shouldProcessUserEvent checks if an event makes it through our filters
  114. func (a *Agent) shouldProcessUserEvent(msg *UserEvent) bool {
  115. // Check the version
  116. if msg.Version > userEventMaxVersion {
  117. a.logger.Printf("[WARN] agent: Event version %d may have unsupported features (%s)",
  118. msg.Version, msg.Name)
  119. }
  120. // Apply the filters
  121. if msg.NodeFilter != "" {
  122. re, err := regexp.Compile(msg.NodeFilter)
  123. if err != nil {
  124. a.logger.Printf("[ERR] agent: Failed to parse node filter '%s' for event '%s': %v",
  125. msg.NodeFilter, msg.Name, err)
  126. return false
  127. }
  128. if !re.MatchString(a.config.NodeName) {
  129. return false
  130. }
  131. }
  132. if msg.ServiceFilter != "" {
  133. re, err := regexp.Compile(msg.ServiceFilter)
  134. if err != nil {
  135. a.logger.Printf("[ERR] agent: Failed to parse service filter '%s' for event '%s': %v",
  136. msg.ServiceFilter, msg.Name, err)
  137. return false
  138. }
  139. var tagRe *regexp.Regexp
  140. if msg.TagFilter != "" {
  141. re, err := regexp.Compile(msg.TagFilter)
  142. if err != nil {
  143. a.logger.Printf("[ERR] agent: Failed to parse tag filter '%s' for event '%s': %v",
  144. msg.TagFilter, msg.Name, err)
  145. return false
  146. }
  147. tagRe = re
  148. }
  149. // Scan for a match
  150. services := a.state.Services()
  151. found := false
  152. OUTER:
  153. for name, info := range services {
  154. // Check the service name
  155. if !re.MatchString(name) {
  156. continue
  157. }
  158. if tagRe == nil {
  159. found = true
  160. break
  161. }
  162. // Look for a matching tag
  163. for _, tag := range info.Tags {
  164. if !tagRe.MatchString(tag) {
  165. continue
  166. }
  167. found = true
  168. break OUTER
  169. }
  170. }
  171. // No matching services
  172. if !found {
  173. return false
  174. }
  175. }
  176. return true
  177. }
  178. // ingestUserEvent is used to process an event that passes filtering
  179. func (a *Agent) ingestUserEvent(msg *UserEvent) {
  180. // Special handling for internal events
  181. switch msg.Name {
  182. case remoteExecName:
  183. if a.config.DisableRemoteExec {
  184. a.logger.Printf("[INFO] agent: ignoring remote exec event (%s), disabled.", msg.ID)
  185. } else {
  186. go a.handleRemoteExec(msg)
  187. }
  188. return
  189. default:
  190. a.logger.Printf("[DEBUG] agent: new event: %s (%s)", msg.Name, msg.ID)
  191. }
  192. a.eventLock.Lock()
  193. defer func() {
  194. a.eventLock.Unlock()
  195. a.eventNotify.Notify()
  196. }()
  197. idx := a.eventIndex
  198. a.eventBuf[idx] = msg
  199. a.eventIndex = (idx + 1) % len(a.eventBuf)
  200. }
  201. // UserEvents is used to return a slice of the most recent
  202. // user events.
  203. func (a *Agent) UserEvents() []*UserEvent {
  204. n := len(a.eventBuf)
  205. out := make([]*UserEvent, n)
  206. a.eventLock.RLock()
  207. defer a.eventLock.RUnlock()
  208. // Check if the buffer is full
  209. if a.eventBuf[a.eventIndex] != nil {
  210. if a.eventIndex == 0 {
  211. copy(out, a.eventBuf)
  212. } else {
  213. copy(out, a.eventBuf[a.eventIndex:])
  214. copy(out[n-a.eventIndex:], a.eventBuf[:a.eventIndex])
  215. }
  216. } else {
  217. // We haven't filled the buffer yet
  218. copy(out, a.eventBuf[:a.eventIndex])
  219. out = out[:a.eventIndex]
  220. }
  221. return out
  222. }
  223. // LastUserEvent is used to return the lastest user event.
  224. // This will return nil if there is no recent event.
  225. func (a *Agent) LastUserEvent() *UserEvent {
  226. a.eventLock.RLock()
  227. defer a.eventLock.RUnlock()
  228. n := len(a.eventBuf)
  229. idx := (((a.eventIndex - 1) % n) + n) % n
  230. return a.eventBuf[idx]
  231. }