events.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. gogotypes "github.com/gogo/protobuf/types"
  13. swarmapi "github.com/moby/swarmkit/v2/api"
  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 name := hostName(); name != "" {
  79. attributes["name"] = name
  80. }
  81. daemon.EventsService.Log(action, events.DaemonEventType, events.Actor{
  82. ID: daemon.id,
  83. Attributes: attributes,
  84. })
  85. }
  86. }
  87. // SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
  88. func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
  89. ef := daemonevents.NewFilter(filter)
  90. return daemon.EventsService.SubscribeTopic(since, until, ef)
  91. }
  92. // UnsubscribeFromEvents stops the event subscription for a client by closing the
  93. // channel where the daemon sends events to.
  94. func (daemon *Daemon) UnsubscribeFromEvents(listener chan interface{}) {
  95. daemon.EventsService.Evict(listener)
  96. }
  97. // copyAttributes guarantees that labels are not mutated by event triggers.
  98. func copyAttributes(attributes, labels map[string]string) {
  99. if labels == nil {
  100. return
  101. }
  102. for k, v := range labels {
  103. attributes[k] = v
  104. }
  105. }
  106. // ProcessClusterNotifications gets changes from store and add them to event list
  107. func (daemon *Daemon) ProcessClusterNotifications(ctx context.Context, watchStream chan *swarmapi.WatchMessage) {
  108. for {
  109. select {
  110. case <-ctx.Done():
  111. return
  112. case message, ok := <-watchStream:
  113. if !ok {
  114. logrus.Debug("cluster event channel has stopped")
  115. return
  116. }
  117. daemon.generateClusterEvent(message)
  118. }
  119. }
  120. }
  121. func (daemon *Daemon) generateClusterEvent(msg *swarmapi.WatchMessage) {
  122. for _, event := range msg.Events {
  123. if event.Object == nil {
  124. logrus.Errorf("event without object: %v", event)
  125. continue
  126. }
  127. switch v := event.Object.GetObject().(type) {
  128. case *swarmapi.Object_Node:
  129. daemon.logNodeEvent(event.Action, v.Node, event.OldObject.GetNode())
  130. case *swarmapi.Object_Service:
  131. daemon.logServiceEvent(event.Action, v.Service, event.OldObject.GetService())
  132. case *swarmapi.Object_Network:
  133. daemon.logNetworkEvent(event.Action, v.Network, event.OldObject.GetNetwork())
  134. case *swarmapi.Object_Secret:
  135. daemon.logSecretEvent(event.Action, v.Secret, event.OldObject.GetSecret())
  136. case *swarmapi.Object_Config:
  137. daemon.logConfigEvent(event.Action, v.Config, event.OldObject.GetConfig())
  138. default:
  139. logrus.Warnf("unrecognized event: %v", event)
  140. }
  141. }
  142. }
  143. func (daemon *Daemon) logNetworkEvent(action swarmapi.WatchActionKind, net *swarmapi.Network, oldNet *swarmapi.Network) {
  144. attributes := map[string]string{
  145. "name": net.Spec.Annotations.Name,
  146. }
  147. eventTime := eventTimestamp(net.Meta, action)
  148. daemon.logClusterEvent(action, net.ID, "network", attributes, eventTime)
  149. }
  150. func (daemon *Daemon) logSecretEvent(action swarmapi.WatchActionKind, secret *swarmapi.Secret, oldSecret *swarmapi.Secret) {
  151. attributes := map[string]string{
  152. "name": secret.Spec.Annotations.Name,
  153. }
  154. eventTime := eventTimestamp(secret.Meta, action)
  155. daemon.logClusterEvent(action, secret.ID, "secret", attributes, eventTime)
  156. }
  157. func (daemon *Daemon) logConfigEvent(action swarmapi.WatchActionKind, config *swarmapi.Config, oldConfig *swarmapi.Config) {
  158. attributes := map[string]string{
  159. "name": config.Spec.Annotations.Name,
  160. }
  161. eventTime := eventTimestamp(config.Meta, action)
  162. daemon.logClusterEvent(action, config.ID, "config", attributes, eventTime)
  163. }
  164. func (daemon *Daemon) logNodeEvent(action swarmapi.WatchActionKind, node *swarmapi.Node, oldNode *swarmapi.Node) {
  165. name := node.Spec.Annotations.Name
  166. if name == "" && node.Description != nil {
  167. name = node.Description.Hostname
  168. }
  169. attributes := map[string]string{
  170. "name": name,
  171. }
  172. eventTime := eventTimestamp(node.Meta, action)
  173. // In an update event, display the changes in attributes
  174. if action == swarmapi.WatchActionKindUpdate && oldNode != nil {
  175. if node.Spec.Availability != oldNode.Spec.Availability {
  176. attributes["availability.old"] = strings.ToLower(oldNode.Spec.Availability.String())
  177. attributes["availability.new"] = strings.ToLower(node.Spec.Availability.String())
  178. }
  179. if node.Role != oldNode.Role {
  180. attributes["role.old"] = strings.ToLower(oldNode.Role.String())
  181. attributes["role.new"] = strings.ToLower(node.Role.String())
  182. }
  183. if node.Status.State != oldNode.Status.State {
  184. attributes["state.old"] = strings.ToLower(oldNode.Status.State.String())
  185. attributes["state.new"] = strings.ToLower(node.Status.State.String())
  186. }
  187. // This handles change within manager role
  188. if node.ManagerStatus != nil && oldNode.ManagerStatus != nil {
  189. // leader change
  190. if node.ManagerStatus.Leader != oldNode.ManagerStatus.Leader {
  191. if node.ManagerStatus.Leader {
  192. attributes["leader.old"] = "false"
  193. attributes["leader.new"] = "true"
  194. } else {
  195. attributes["leader.old"] = "true"
  196. attributes["leader.new"] = "false"
  197. }
  198. }
  199. if node.ManagerStatus.Reachability != oldNode.ManagerStatus.Reachability {
  200. attributes["reachability.old"] = strings.ToLower(oldNode.ManagerStatus.Reachability.String())
  201. attributes["reachability.new"] = strings.ToLower(node.ManagerStatus.Reachability.String())
  202. }
  203. }
  204. }
  205. daemon.logClusterEvent(action, node.ID, "node", attributes, eventTime)
  206. }
  207. func (daemon *Daemon) logServiceEvent(action swarmapi.WatchActionKind, service *swarmapi.Service, oldService *swarmapi.Service) {
  208. attributes := map[string]string{
  209. "name": service.Spec.Annotations.Name,
  210. }
  211. eventTime := eventTimestamp(service.Meta, action)
  212. if action == swarmapi.WatchActionKindUpdate && oldService != nil {
  213. // check image
  214. if x, ok := service.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok {
  215. containerSpec := x.Container
  216. if y, ok := oldService.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok {
  217. oldContainerSpec := y.Container
  218. if containerSpec.Image != oldContainerSpec.Image {
  219. attributes["image.old"] = oldContainerSpec.Image
  220. attributes["image.new"] = containerSpec.Image
  221. }
  222. } else {
  223. // This should not happen.
  224. logrus.Errorf("service %s runtime changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.Task.GetRuntime(), service.Spec.Task.GetRuntime())
  225. }
  226. }
  227. // check replicated count change
  228. if x, ok := service.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok {
  229. replicas := x.Replicated.Replicas
  230. if y, ok := oldService.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok {
  231. oldReplicas := y.Replicated.Replicas
  232. if replicas != oldReplicas {
  233. attributes["replicas.old"] = strconv.FormatUint(oldReplicas, 10)
  234. attributes["replicas.new"] = strconv.FormatUint(replicas, 10)
  235. }
  236. } else {
  237. // This should not happen.
  238. logrus.Errorf("service %s mode changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.GetMode(), service.Spec.GetMode())
  239. }
  240. }
  241. if service.UpdateStatus != nil {
  242. if oldService.UpdateStatus == nil {
  243. attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String())
  244. } else if service.UpdateStatus.State != oldService.UpdateStatus.State {
  245. attributes["updatestate.old"] = strings.ToLower(oldService.UpdateStatus.State.String())
  246. attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String())
  247. }
  248. }
  249. }
  250. daemon.logClusterEvent(action, service.ID, "service", attributes, eventTime)
  251. }
  252. func (daemon *Daemon) logClusterEvent(action swarmapi.WatchActionKind, id, eventType string, attributes map[string]string, eventTime time.Time) {
  253. actor := events.Actor{
  254. ID: id,
  255. Attributes: attributes,
  256. }
  257. jm := events.Message{
  258. Action: clusterEventAction[action],
  259. Type: eventType,
  260. Actor: actor,
  261. Scope: "swarm",
  262. Time: eventTime.UTC().Unix(),
  263. TimeNano: eventTime.UTC().UnixNano(),
  264. }
  265. daemon.EventsService.PublishMessage(jm)
  266. }
  267. func eventTimestamp(meta swarmapi.Meta, action swarmapi.WatchActionKind) time.Time {
  268. var eventTime time.Time
  269. switch action {
  270. case swarmapi.WatchActionKindCreate:
  271. eventTime, _ = gogotypes.TimestampFromProto(meta.CreatedAt)
  272. case swarmapi.WatchActionKindUpdate:
  273. eventTime, _ = gogotypes.TimestampFromProto(meta.UpdatedAt)
  274. case swarmapi.WatchActionKindRemove:
  275. // There is no timestamp from store message for remove operations.
  276. // Use current time.
  277. eventTime = time.Now()
  278. }
  279. return eventTime
  280. }