github.com/LazyboyChen7/engine@v17.12.1-ce-rc2+incompatible/daemon/events.go (about) 1 package daemon 2 3 import ( 4 "context" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/docker/docker/api/types/events" 10 "github.com/docker/docker/api/types/filters" 11 "github.com/docker/docker/container" 12 daemonevents "github.com/docker/docker/daemon/events" 13 "github.com/docker/libnetwork" 14 swarmapi "github.com/docker/swarmkit/api" 15 gogotypes "github.com/gogo/protobuf/types" 16 "github.com/sirupsen/logrus" 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 case *swarmapi.Object_Config: 179 daemon.logConfigEvent(event.Action, v.Config, event.OldObject.GetConfig()) 180 default: 181 logrus.Warnf("unrecognized event: %v", event) 182 } 183 } 184 } 185 186 func (daemon *Daemon) logNetworkEvent(action swarmapi.WatchActionKind, net *swarmapi.Network, oldNet *swarmapi.Network) { 187 attributes := map[string]string{ 188 "name": net.Spec.Annotations.Name, 189 } 190 eventTime := eventTimestamp(net.Meta, action) 191 daemon.logClusterEvent(action, net.ID, "network", attributes, eventTime) 192 } 193 194 func (daemon *Daemon) logSecretEvent(action swarmapi.WatchActionKind, secret *swarmapi.Secret, oldSecret *swarmapi.Secret) { 195 attributes := map[string]string{ 196 "name": secret.Spec.Annotations.Name, 197 } 198 eventTime := eventTimestamp(secret.Meta, action) 199 daemon.logClusterEvent(action, secret.ID, "secret", attributes, eventTime) 200 } 201 202 func (daemon *Daemon) logConfigEvent(action swarmapi.WatchActionKind, config *swarmapi.Config, oldConfig *swarmapi.Config) { 203 attributes := map[string]string{ 204 "name": config.Spec.Annotations.Name, 205 } 206 eventTime := eventTimestamp(config.Meta, action) 207 daemon.logClusterEvent(action, config.ID, "config", attributes, eventTime) 208 } 209 210 func (daemon *Daemon) logNodeEvent(action swarmapi.WatchActionKind, node *swarmapi.Node, oldNode *swarmapi.Node) { 211 name := node.Spec.Annotations.Name 212 if name == "" && node.Description != nil { 213 name = node.Description.Hostname 214 } 215 attributes := map[string]string{ 216 "name": name, 217 } 218 eventTime := eventTimestamp(node.Meta, action) 219 // In an update event, display the changes in attributes 220 if action == swarmapi.WatchActionKindUpdate && oldNode != nil { 221 if node.Spec.Availability != oldNode.Spec.Availability { 222 attributes["availability.old"] = strings.ToLower(oldNode.Spec.Availability.String()) 223 attributes["availability.new"] = strings.ToLower(node.Spec.Availability.String()) 224 } 225 if node.Role != oldNode.Role { 226 attributes["role.old"] = strings.ToLower(oldNode.Role.String()) 227 attributes["role.new"] = strings.ToLower(node.Role.String()) 228 } 229 if node.Status.State != oldNode.Status.State { 230 attributes["state.old"] = strings.ToLower(oldNode.Status.State.String()) 231 attributes["state.new"] = strings.ToLower(node.Status.State.String()) 232 } 233 // This handles change within manager role 234 if node.ManagerStatus != nil && oldNode.ManagerStatus != nil { 235 // leader change 236 if node.ManagerStatus.Leader != oldNode.ManagerStatus.Leader { 237 if node.ManagerStatus.Leader { 238 attributes["leader.old"] = "false" 239 attributes["leader.new"] = "true" 240 } else { 241 attributes["leader.old"] = "true" 242 attributes["leader.new"] = "false" 243 } 244 } 245 if node.ManagerStatus.Reachability != oldNode.ManagerStatus.Reachability { 246 attributes["reachability.old"] = strings.ToLower(oldNode.ManagerStatus.Reachability.String()) 247 attributes["reachability.new"] = strings.ToLower(node.ManagerStatus.Reachability.String()) 248 } 249 } 250 } 251 252 daemon.logClusterEvent(action, node.ID, "node", attributes, eventTime) 253 } 254 255 func (daemon *Daemon) logServiceEvent(action swarmapi.WatchActionKind, service *swarmapi.Service, oldService *swarmapi.Service) { 256 attributes := map[string]string{ 257 "name": service.Spec.Annotations.Name, 258 } 259 eventTime := eventTimestamp(service.Meta, action) 260 261 if action == swarmapi.WatchActionKindUpdate && oldService != nil { 262 // check image 263 if x, ok := service.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok { 264 containerSpec := x.Container 265 if y, ok := oldService.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok { 266 oldContainerSpec := y.Container 267 if containerSpec.Image != oldContainerSpec.Image { 268 attributes["image.old"] = oldContainerSpec.Image 269 attributes["image.new"] = containerSpec.Image 270 } 271 } else { 272 // This should not happen. 273 logrus.Errorf("service %s runtime changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.Task.GetRuntime(), service.Spec.Task.GetRuntime()) 274 } 275 } 276 // check replicated count change 277 if x, ok := service.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok { 278 replicas := x.Replicated.Replicas 279 if y, ok := oldService.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok { 280 oldReplicas := y.Replicated.Replicas 281 if replicas != oldReplicas { 282 attributes["replicas.old"] = strconv.FormatUint(oldReplicas, 10) 283 attributes["replicas.new"] = strconv.FormatUint(replicas, 10) 284 } 285 } else { 286 // This should not happen. 287 logrus.Errorf("service %s mode changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.GetMode(), service.Spec.GetMode()) 288 } 289 } 290 if service.UpdateStatus != nil { 291 if oldService.UpdateStatus == nil { 292 attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String()) 293 } else if service.UpdateStatus.State != oldService.UpdateStatus.State { 294 attributes["updatestate.old"] = strings.ToLower(oldService.UpdateStatus.State.String()) 295 attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String()) 296 } 297 } 298 } 299 daemon.logClusterEvent(action, service.ID, "service", attributes, eventTime) 300 } 301 302 func (daemon *Daemon) logClusterEvent(action swarmapi.WatchActionKind, id, eventType string, attributes map[string]string, eventTime time.Time) { 303 actor := events.Actor{ 304 ID: id, 305 Attributes: attributes, 306 } 307 308 jm := events.Message{ 309 Action: clusterEventAction[action], 310 Type: eventType, 311 Actor: actor, 312 Scope: "swarm", 313 Time: eventTime.UTC().Unix(), 314 TimeNano: eventTime.UTC().UnixNano(), 315 } 316 daemon.EventsService.PublishMessage(jm) 317 } 318 319 func eventTimestamp(meta swarmapi.Meta, action swarmapi.WatchActionKind) time.Time { 320 var eventTime time.Time 321 switch action { 322 case swarmapi.WatchActionKindCreate: 323 eventTime, _ = gogotypes.TimestampFromProto(meta.CreatedAt) 324 case swarmapi.WatchActionKindUpdate: 325 eventTime, _ = gogotypes.TimestampFromProto(meta.UpdatedAt) 326 case swarmapi.WatchActionKindRemove: 327 // There is no timestamp from store message for remove operations. 328 // Use current time. 329 eventTime = time.Now() 330 } 331 return eventTime 332 }