events.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. package daemon // import "github.com/docker/docker/daemon"
  2. import (
  3. "context"
  4. "strconv"
  5. "strings"
  6. "time"
  7. "github.com/docker/docker/api/types/events"
  8. "github.com/docker/docker/api/types/filters"
  9. "github.com/docker/docker/container"
  10. daemonevents "github.com/docker/docker/daemon/events"
  11. "github.com/docker/docker/libnetwork"
  12. swarmapi "github.com/docker/swarmkit/api"
  13. gogotypes "github.com/gogo/protobuf/types"
  14. "github.com/sirupsen/logrus"
  15. )
  16. var (
  17. clusterEventAction = map[swarmapi.WatchActionKind]string{
  18. swarmapi.WatchActionKindCreate: "create",
  19. swarmapi.WatchActionKindUpdate: "update",
  20. swarmapi.WatchActionKindRemove: "remove",
  21. }
  22. )
  23. // LogContainerEvent generates an event related to a container with only the default attributes.
  24. func (daemon *Daemon) LogContainerEvent(container *container.Container, action string) {
  25. daemon.LogContainerEventWithAttributes(container, action, map[string]string{})
  26. }
  27. // LogContainerEventWithAttributes generates an event related to a container with specific given attributes.
  28. func (daemon *Daemon) LogContainerEventWithAttributes(container *container.Container, action string, attributes map[string]string) {
  29. copyAttributes(attributes, container.Config.Labels)
  30. if container.Config.Image != "" {
  31. attributes["image"] = container.Config.Image
  32. }
  33. attributes["name"] = strings.TrimLeft(container.Name, "/")
  34. actor := events.Actor{
  35. ID: container.ID,
  36. Attributes: attributes,
  37. }
  38. daemon.EventsService.Log(action, events.ContainerEventType, actor)
  39. }
  40. // LogPluginEvent generates an event related to a plugin with only the default attributes.
  41. func (daemon *Daemon) LogPluginEvent(pluginID, refName, action string) {
  42. daemon.LogPluginEventWithAttributes(pluginID, refName, action, map[string]string{})
  43. }
  44. // LogPluginEventWithAttributes generates an event related to a plugin with specific given attributes.
  45. func (daemon *Daemon) LogPluginEventWithAttributes(pluginID, refName, action string, attributes map[string]string) {
  46. attributes["name"] = refName
  47. actor := events.Actor{
  48. ID: pluginID,
  49. Attributes: attributes,
  50. }
  51. daemon.EventsService.Log(action, events.PluginEventType, actor)
  52. }
  53. // LogVolumeEvent generates an event related to a volume.
  54. func (daemon *Daemon) LogVolumeEvent(volumeID, action string, attributes map[string]string) {
  55. actor := events.Actor{
  56. ID: volumeID,
  57. Attributes: attributes,
  58. }
  59. daemon.EventsService.Log(action, events.VolumeEventType, actor)
  60. }
  61. // LogNetworkEvent generates an event related to a network with only the default attributes.
  62. func (daemon *Daemon) LogNetworkEvent(nw libnetwork.Network, action string) {
  63. daemon.LogNetworkEventWithAttributes(nw, action, map[string]string{})
  64. }
  65. // LogNetworkEventWithAttributes generates an event related to a network with specific given attributes.
  66. func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, action string, attributes map[string]string) {
  67. attributes["name"] = nw.Name()
  68. attributes["type"] = nw.Type()
  69. actor := events.Actor{
  70. ID: nw.ID(),
  71. Attributes: attributes,
  72. }
  73. daemon.EventsService.Log(action, events.NetworkEventType, actor)
  74. }
  75. // LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
  76. func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
  77. if daemon.EventsService != nil {
  78. if info := daemon.SystemInfo(); info.Name != "" {
  79. attributes["name"] = info.Name
  80. }
  81. actor := events.Actor{
  82. ID: daemon.ID,
  83. Attributes: attributes,
  84. }
  85. daemon.EventsService.Log(action, events.DaemonEventType, actor)
  86. }
  87. }
  88. // SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
  89. func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
  90. ef := daemonevents.NewFilter(filter)
  91. return daemon.EventsService.SubscribeTopic(since, until, ef)
  92. }
  93. // UnsubscribeFromEvents stops the event subscription for a client by closing the
  94. // channel where the daemon sends events to.
  95. func (daemon *Daemon) UnsubscribeFromEvents(listener chan interface{}) {
  96. daemon.EventsService.Evict(listener)
  97. }
  98. // copyAttributes guarantees that labels are not mutated by event triggers.
  99. func copyAttributes(attributes, labels map[string]string) {
  100. if labels == nil {
  101. return
  102. }
  103. for k, v := range labels {
  104. attributes[k] = v
  105. }
  106. }
  107. // ProcessClusterNotifications gets changes from store and add them to event list
  108. func (daemon *Daemon) ProcessClusterNotifications(ctx context.Context, watchStream chan *swarmapi.WatchMessage) {
  109. for {
  110. select {
  111. case <-ctx.Done():
  112. return
  113. case message, ok := <-watchStream:
  114. if !ok {
  115. logrus.Debug("cluster event channel has stopped")
  116. return
  117. }
  118. daemon.generateClusterEvent(message)
  119. }
  120. }
  121. }
  122. func (daemon *Daemon) generateClusterEvent(msg *swarmapi.WatchMessage) {
  123. for _, event := range msg.Events {
  124. if event.Object == nil {
  125. logrus.Errorf("event without object: %v", event)
  126. continue
  127. }
  128. switch v := event.Object.GetObject().(type) {
  129. case *swarmapi.Object_Node:
  130. daemon.logNodeEvent(event.Action, v.Node, event.OldObject.GetNode())
  131. case *swarmapi.Object_Service:
  132. daemon.logServiceEvent(event.Action, v.Service, event.OldObject.GetService())
  133. case *swarmapi.Object_Network:
  134. daemon.logNetworkEvent(event.Action, v.Network, event.OldObject.GetNetwork())
  135. case *swarmapi.Object_Secret:
  136. daemon.logSecretEvent(event.Action, v.Secret, event.OldObject.GetSecret())
  137. case *swarmapi.Object_Config:
  138. daemon.logConfigEvent(event.Action, v.Config, event.OldObject.GetConfig())
  139. default:
  140. logrus.Warnf("unrecognized event: %v", event)
  141. }
  142. }
  143. }
  144. func (daemon *Daemon) logNetworkEvent(action swarmapi.WatchActionKind, net *swarmapi.Network, oldNet *swarmapi.Network) {
  145. attributes := map[string]string{
  146. "name": net.Spec.Annotations.Name,
  147. }
  148. eventTime := eventTimestamp(net.Meta, action)
  149. daemon.logClusterEvent(action, net.ID, "network", attributes, eventTime)
  150. }
  151. func (daemon *Daemon) logSecretEvent(action swarmapi.WatchActionKind, secret *swarmapi.Secret, oldSecret *swarmapi.Secret) {
  152. attributes := map[string]string{
  153. "name": secret.Spec.Annotations.Name,
  154. }
  155. eventTime := eventTimestamp(secret.Meta, action)
  156. daemon.logClusterEvent(action, secret.ID, "secret", attributes, eventTime)
  157. }
  158. func (daemon *Daemon) logConfigEvent(action swarmapi.WatchActionKind, config *swarmapi.Config, oldConfig *swarmapi.Config) {
  159. attributes := map[string]string{
  160. "name": config.Spec.Annotations.Name,
  161. }
  162. eventTime := eventTimestamp(config.Meta, action)
  163. daemon.logClusterEvent(action, config.ID, "config", attributes, eventTime)
  164. }
  165. func (daemon *Daemon) logNodeEvent(action swarmapi.WatchActionKind, node *swarmapi.Node, oldNode *swarmapi.Node) {
  166. name := node.Spec.Annotations.Name
  167. if name == "" && node.Description != nil {
  168. name = node.Description.Hostname
  169. }
  170. attributes := map[string]string{
  171. "name": name,
  172. }
  173. eventTime := eventTimestamp(node.Meta, action)
  174. // In an update event, display the changes in attributes
  175. if action == swarmapi.WatchActionKindUpdate && oldNode != nil {
  176. if node.Spec.Availability != oldNode.Spec.Availability {
  177. attributes["availability.old"] = strings.ToLower(oldNode.Spec.Availability.String())
  178. attributes["availability.new"] = strings.ToLower(node.Spec.Availability.String())
  179. }
  180. if node.Role != oldNode.Role {
  181. attributes["role.old"] = strings.ToLower(oldNode.Role.String())
  182. attributes["role.new"] = strings.ToLower(node.Role.String())
  183. }
  184. if node.Status.State != oldNode.Status.State {
  185. attributes["state.old"] = strings.ToLower(oldNode.Status.State.String())
  186. attributes["state.new"] = strings.ToLower(node.Status.State.String())
  187. }
  188. // This handles change within manager role
  189. if node.ManagerStatus != nil && oldNode.ManagerStatus != nil {
  190. // leader change
  191. if node.ManagerStatus.Leader != oldNode.ManagerStatus.Leader {
  192. if node.ManagerStatus.Leader {
  193. attributes["leader.old"] = "false"
  194. attributes["leader.new"] = "true"
  195. } else {
  196. attributes["leader.old"] = "true"
  197. attributes["leader.new"] = "false"
  198. }
  199. }
  200. if node.ManagerStatus.Reachability != oldNode.ManagerStatus.Reachability {
  201. attributes["reachability.old"] = strings.ToLower(oldNode.ManagerStatus.Reachability.String())
  202. attributes["reachability.new"] = strings.ToLower(node.ManagerStatus.Reachability.String())
  203. }
  204. }
  205. }
  206. daemon.logClusterEvent(action, node.ID, "node", attributes, eventTime)
  207. }
  208. func (daemon *Daemon) logServiceEvent(action swarmapi.WatchActionKind, service *swarmapi.Service, oldService *swarmapi.Service) {
  209. attributes := map[string]string{
  210. "name": service.Spec.Annotations.Name,
  211. }
  212. eventTime := eventTimestamp(service.Meta, action)
  213. if action == swarmapi.WatchActionKindUpdate && oldService != nil {
  214. // check image
  215. if x, ok := service.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok {
  216. containerSpec := x.Container
  217. if y, ok := oldService.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok {
  218. oldContainerSpec := y.Container
  219. if containerSpec.Image != oldContainerSpec.Image {
  220. attributes["image.old"] = oldContainerSpec.Image
  221. attributes["image.new"] = containerSpec.Image
  222. }
  223. } else {
  224. // This should not happen.
  225. logrus.Errorf("service %s runtime changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.Task.GetRuntime(), service.Spec.Task.GetRuntime())
  226. }
  227. }
  228. // check replicated count change
  229. if x, ok := service.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok {
  230. replicas := x.Replicated.Replicas
  231. if y, ok := oldService.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok {
  232. oldReplicas := y.Replicated.Replicas
  233. if replicas != oldReplicas {
  234. attributes["replicas.old"] = strconv.FormatUint(oldReplicas, 10)
  235. attributes["replicas.new"] = strconv.FormatUint(replicas, 10)
  236. }
  237. } else {
  238. // This should not happen.
  239. logrus.Errorf("service %s mode changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.GetMode(), service.Spec.GetMode())
  240. }
  241. }
  242. if service.UpdateStatus != nil {
  243. if oldService.UpdateStatus == nil {
  244. attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String())
  245. } else if service.UpdateStatus.State != oldService.UpdateStatus.State {
  246. attributes["updatestate.old"] = strings.ToLower(oldService.UpdateStatus.State.String())
  247. attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String())
  248. }
  249. }
  250. }
  251. daemon.logClusterEvent(action, service.ID, "service", attributes, eventTime)
  252. }
  253. func (daemon *Daemon) logClusterEvent(action swarmapi.WatchActionKind, id, eventType string, attributes map[string]string, eventTime time.Time) {
  254. actor := events.Actor{
  255. ID: id,
  256. Attributes: attributes,
  257. }
  258. jm := events.Message{
  259. Action: clusterEventAction[action],
  260. Type: eventType,
  261. Actor: actor,
  262. Scope: "swarm",
  263. Time: eventTime.UTC().Unix(),
  264. TimeNano: eventTime.UTC().UnixNano(),
  265. }
  266. daemon.EventsService.PublishMessage(jm)
  267. }
  268. func eventTimestamp(meta swarmapi.Meta, action swarmapi.WatchActionKind) time.Time {
  269. var eventTime time.Time
  270. switch action {
  271. case swarmapi.WatchActionKindCreate:
  272. eventTime, _ = gogotypes.TimestampFromProto(meta.CreatedAt)
  273. case swarmapi.WatchActionKindUpdate:
  274. eventTime, _ = gogotypes.TimestampFromProto(meta.UpdatedAt)
  275. case swarmapi.WatchActionKindRemove:
  276. // There is no timestamp from store message for remove operations.
  277. // Use current time.
  278. eventTime = time.Now()
  279. }
  280. return eventTime
  281. }