1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/daemon/events.go
Sebastiaan van Stijn ac2cd5a8f2
daemon: unexport Daemon.ID and Daemon.RegistryService
These are used internally only, and set by daemon.NewDaemon(). If they're
used externally, we should add an accessor added (which may be something
we want to do for daemon.registryService (which should be its own backend)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-03-02 22:19:22 +01:00

308 lines
11 KiB
Go

package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"strconv"
"strings"
"time"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/container"
daemonevents "github.com/docker/docker/daemon/events"
"github.com/docker/docker/libnetwork"
swarmapi "github.com/docker/swarmkit/api"
gogotypes "github.com/gogo/protobuf/types"
"github.com/sirupsen/logrus"
)
var (
clusterEventAction = map[swarmapi.WatchActionKind]string{
swarmapi.WatchActionKindCreate: "create",
swarmapi.WatchActionKindUpdate: "update",
swarmapi.WatchActionKindRemove: "remove",
}
)
// LogContainerEvent generates an event related to a container with only the default attributes.
func (daemon *Daemon) LogContainerEvent(container *container.Container, action string) {
daemon.LogContainerEventWithAttributes(container, action, map[string]string{})
}
// LogContainerEventWithAttributes generates an event related to a container with specific given attributes.
func (daemon *Daemon) LogContainerEventWithAttributes(container *container.Container, action string, attributes map[string]string) {
copyAttributes(attributes, container.Config.Labels)
if container.Config.Image != "" {
attributes["image"] = container.Config.Image
}
attributes["name"] = strings.TrimLeft(container.Name, "/")
actor := events.Actor{
ID: container.ID,
Attributes: attributes,
}
daemon.EventsService.Log(action, events.ContainerEventType, actor)
}
// LogPluginEvent generates an event related to a plugin with only the default attributes.
func (daemon *Daemon) LogPluginEvent(pluginID, refName, action string) {
daemon.LogPluginEventWithAttributes(pluginID, refName, action, map[string]string{})
}
// LogPluginEventWithAttributes generates an event related to a plugin with specific given attributes.
func (daemon *Daemon) LogPluginEventWithAttributes(pluginID, refName, action string, attributes map[string]string) {
attributes["name"] = refName
actor := events.Actor{
ID: pluginID,
Attributes: attributes,
}
daemon.EventsService.Log(action, events.PluginEventType, actor)
}
// LogVolumeEvent generates an event related to a volume.
func (daemon *Daemon) LogVolumeEvent(volumeID, action string, attributes map[string]string) {
actor := events.Actor{
ID: volumeID,
Attributes: attributes,
}
daemon.EventsService.Log(action, events.VolumeEventType, actor)
}
// LogNetworkEvent generates an event related to a network with only the default attributes.
func (daemon *Daemon) LogNetworkEvent(nw libnetwork.Network, action string) {
daemon.LogNetworkEventWithAttributes(nw, action, map[string]string{})
}
// LogNetworkEventWithAttributes generates an event related to a network with specific given attributes.
func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, action string, attributes map[string]string) {
attributes["name"] = nw.Name()
attributes["type"] = nw.Type()
actor := events.Actor{
ID: nw.ID(),
Attributes: attributes,
}
daemon.EventsService.Log(action, events.NetworkEventType, actor)
}
// LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
if daemon.EventsService != nil {
if info := daemon.SystemInfo(); info.Name != "" {
attributes["name"] = info.Name
}
actor := events.Actor{
ID: daemon.id,
Attributes: attributes,
}
daemon.EventsService.Log(action, events.DaemonEventType, actor)
}
}
// SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
ef := daemonevents.NewFilter(filter)
return daemon.EventsService.SubscribeTopic(since, until, ef)
}
// UnsubscribeFromEvents stops the event subscription for a client by closing the
// channel where the daemon sends events to.
func (daemon *Daemon) UnsubscribeFromEvents(listener chan interface{}) {
daemon.EventsService.Evict(listener)
}
// copyAttributes guarantees that labels are not mutated by event triggers.
func copyAttributes(attributes, labels map[string]string) {
if labels == nil {
return
}
for k, v := range labels {
attributes[k] = v
}
}
// ProcessClusterNotifications gets changes from store and add them to event list
func (daemon *Daemon) ProcessClusterNotifications(ctx context.Context, watchStream chan *swarmapi.WatchMessage) {
for {
select {
case <-ctx.Done():
return
case message, ok := <-watchStream:
if !ok {
logrus.Debug("cluster event channel has stopped")
return
}
daemon.generateClusterEvent(message)
}
}
}
func (daemon *Daemon) generateClusterEvent(msg *swarmapi.WatchMessage) {
for _, event := range msg.Events {
if event.Object == nil {
logrus.Errorf("event without object: %v", event)
continue
}
switch v := event.Object.GetObject().(type) {
case *swarmapi.Object_Node:
daemon.logNodeEvent(event.Action, v.Node, event.OldObject.GetNode())
case *swarmapi.Object_Service:
daemon.logServiceEvent(event.Action, v.Service, event.OldObject.GetService())
case *swarmapi.Object_Network:
daemon.logNetworkEvent(event.Action, v.Network, event.OldObject.GetNetwork())
case *swarmapi.Object_Secret:
daemon.logSecretEvent(event.Action, v.Secret, event.OldObject.GetSecret())
case *swarmapi.Object_Config:
daemon.logConfigEvent(event.Action, v.Config, event.OldObject.GetConfig())
default:
logrus.Warnf("unrecognized event: %v", event)
}
}
}
func (daemon *Daemon) logNetworkEvent(action swarmapi.WatchActionKind, net *swarmapi.Network, oldNet *swarmapi.Network) {
attributes := map[string]string{
"name": net.Spec.Annotations.Name,
}
eventTime := eventTimestamp(net.Meta, action)
daemon.logClusterEvent(action, net.ID, "network", attributes, eventTime)
}
func (daemon *Daemon) logSecretEvent(action swarmapi.WatchActionKind, secret *swarmapi.Secret, oldSecret *swarmapi.Secret) {
attributes := map[string]string{
"name": secret.Spec.Annotations.Name,
}
eventTime := eventTimestamp(secret.Meta, action)
daemon.logClusterEvent(action, secret.ID, "secret", attributes, eventTime)
}
func (daemon *Daemon) logConfigEvent(action swarmapi.WatchActionKind, config *swarmapi.Config, oldConfig *swarmapi.Config) {
attributes := map[string]string{
"name": config.Spec.Annotations.Name,
}
eventTime := eventTimestamp(config.Meta, action)
daemon.logClusterEvent(action, config.ID, "config", attributes, eventTime)
}
func (daemon *Daemon) logNodeEvent(action swarmapi.WatchActionKind, node *swarmapi.Node, oldNode *swarmapi.Node) {
name := node.Spec.Annotations.Name
if name == "" && node.Description != nil {
name = node.Description.Hostname
}
attributes := map[string]string{
"name": name,
}
eventTime := eventTimestamp(node.Meta, action)
// In an update event, display the changes in attributes
if action == swarmapi.WatchActionKindUpdate && oldNode != nil {
if node.Spec.Availability != oldNode.Spec.Availability {
attributes["availability.old"] = strings.ToLower(oldNode.Spec.Availability.String())
attributes["availability.new"] = strings.ToLower(node.Spec.Availability.String())
}
if node.Role != oldNode.Role {
attributes["role.old"] = strings.ToLower(oldNode.Role.String())
attributes["role.new"] = strings.ToLower(node.Role.String())
}
if node.Status.State != oldNode.Status.State {
attributes["state.old"] = strings.ToLower(oldNode.Status.State.String())
attributes["state.new"] = strings.ToLower(node.Status.State.String())
}
// This handles change within manager role
if node.ManagerStatus != nil && oldNode.ManagerStatus != nil {
// leader change
if node.ManagerStatus.Leader != oldNode.ManagerStatus.Leader {
if node.ManagerStatus.Leader {
attributes["leader.old"] = "false"
attributes["leader.new"] = "true"
} else {
attributes["leader.old"] = "true"
attributes["leader.new"] = "false"
}
}
if node.ManagerStatus.Reachability != oldNode.ManagerStatus.Reachability {
attributes["reachability.old"] = strings.ToLower(oldNode.ManagerStatus.Reachability.String())
attributes["reachability.new"] = strings.ToLower(node.ManagerStatus.Reachability.String())
}
}
}
daemon.logClusterEvent(action, node.ID, "node", attributes, eventTime)
}
func (daemon *Daemon) logServiceEvent(action swarmapi.WatchActionKind, service *swarmapi.Service, oldService *swarmapi.Service) {
attributes := map[string]string{
"name": service.Spec.Annotations.Name,
}
eventTime := eventTimestamp(service.Meta, action)
if action == swarmapi.WatchActionKindUpdate && oldService != nil {
// check image
if x, ok := service.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok {
containerSpec := x.Container
if y, ok := oldService.Spec.Task.GetRuntime().(*swarmapi.TaskSpec_Container); ok {
oldContainerSpec := y.Container
if containerSpec.Image != oldContainerSpec.Image {
attributes["image.old"] = oldContainerSpec.Image
attributes["image.new"] = containerSpec.Image
}
} else {
// This should not happen.
logrus.Errorf("service %s runtime changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.Task.GetRuntime(), service.Spec.Task.GetRuntime())
}
}
// check replicated count change
if x, ok := service.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok {
replicas := x.Replicated.Replicas
if y, ok := oldService.Spec.GetMode().(*swarmapi.ServiceSpec_Replicated); ok {
oldReplicas := y.Replicated.Replicas
if replicas != oldReplicas {
attributes["replicas.old"] = strconv.FormatUint(oldReplicas, 10)
attributes["replicas.new"] = strconv.FormatUint(replicas, 10)
}
} else {
// This should not happen.
logrus.Errorf("service %s mode changed from %T to %T", service.Spec.Annotations.Name, oldService.Spec.GetMode(), service.Spec.GetMode())
}
}
if service.UpdateStatus != nil {
if oldService.UpdateStatus == nil {
attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String())
} else if service.UpdateStatus.State != oldService.UpdateStatus.State {
attributes["updatestate.old"] = strings.ToLower(oldService.UpdateStatus.State.String())
attributes["updatestate.new"] = strings.ToLower(service.UpdateStatus.State.String())
}
}
}
daemon.logClusterEvent(action, service.ID, "service", attributes, eventTime)
}
func (daemon *Daemon) logClusterEvent(action swarmapi.WatchActionKind, id, eventType string, attributes map[string]string, eventTime time.Time) {
actor := events.Actor{
ID: id,
Attributes: attributes,
}
jm := events.Message{
Action: clusterEventAction[action],
Type: eventType,
Actor: actor,
Scope: "swarm",
Time: eventTime.UTC().Unix(),
TimeNano: eventTime.UTC().UnixNano(),
}
daemon.EventsService.PublishMessage(jm)
}
func eventTimestamp(meta swarmapi.Meta, action swarmapi.WatchActionKind) time.Time {
var eventTime time.Time
switch action {
case swarmapi.WatchActionKindCreate:
eventTime, _ = gogotypes.TimestampFromProto(meta.CreatedAt)
case swarmapi.WatchActionKindUpdate:
eventTime, _ = gogotypes.TimestampFromProto(meta.UpdatedAt)
case swarmapi.WatchActionKindRemove:
// There is no timestamp from store message for remove operations.
// Use current time.
eventTime = time.Now()
}
return eventTime
}