events.go 11 KB

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