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