package events import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" ) // Filter can filter out docker events from a stream type Filter struct { filter filters.Args } // NewFilter creates a new Filter func NewFilter(filter filters.Args) *Filter { return &Filter{filter: filter} } // Include returns true when the event ev is included by the filters func (ef *Filter) Include(ev events.Message) bool { return ef.matchEvent(ev) && ef.filter.ExactMatch("type", ev.Type) && ef.matchScope(ev.Scope) && ef.matchDaemon(ev) && ef.matchContainer(ev) && ef.matchPlugin(ev) && ef.matchVolume(ev) && ef.matchNetwork(ev) && ef.matchImage(ev) && ef.matchLabels(ev.Actor.Attributes) } func (ef *Filter) matchEvent(ev events.Message) bool { // #25798 if an event filter contains either health_status, exec_create or exec_start without a colon // Let's to a FuzzyMatch instead of an ExactMatch. if ef.filterContains("event", map[string]struct{}{"health_status": {}, "exec_create": {}, "exec_start": {}}) { return ef.filter.FuzzyMatch("event", ev.Action) } return ef.filter.ExactMatch("event", ev.Action) } func (ef *Filter) filterContains(field string, values map[string]struct{}) bool { for _, v := range ef.filter.Get(field) { if _, ok := values[v]; ok { return true } } return false } func (ef *Filter) matchScope(scope string) bool { if !ef.filter.Include("scope") { return true } return ef.filter.ExactMatch("scope", scope) } func (ef *Filter) matchLabels(attributes map[string]string) bool { if !ef.filter.Include("label") { return true } return ef.filter.MatchKVList("label", attributes) } func (ef *Filter) matchDaemon(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.DaemonEventType) } func (ef *Filter) matchContainer(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.ContainerEventType) } func (ef *Filter) matchPlugin(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.PluginEventType) } func (ef *Filter) matchVolume(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.VolumeEventType) } func (ef *Filter) matchNetwork(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.NetworkEventType) } func (ef *Filter) matchService(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.ServiceEventType) } func (ef *Filter) matchNode(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.NodeEventType) } func (ef *Filter) matchSecret(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.SecretEventType) } func (ef *Filter) matchConfig(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.ConfigEventType) } func (ef *Filter) fuzzyMatchName(ev events.Message, eventType string) bool { return ef.filter.FuzzyMatch(eventType, ev.Actor.ID) || ef.filter.FuzzyMatch(eventType, ev.Actor.Attributes["name"]) } // matchImage matches against both event.Actor.ID (for image events) // and event.Actor.Attributes["image"] (for container events), so that any container that was created // from an image will be included in the image events. Also compare both // against the stripped repo name without any tags. func (ef *Filter) matchImage(ev events.Message) bool { id := ev.Actor.ID nameAttr := "image" var imageName string if ev.Type == events.ImageEventType { nameAttr = "name" } if n, ok := ev.Actor.Attributes[nameAttr]; ok { imageName = n } return ef.filter.ExactMatch("image", id) || ef.filter.ExactMatch("image", imageName) || ef.filter.ExactMatch("image", stripTag(id)) || ef.filter.ExactMatch("image", stripTag(imageName)) } func stripTag(image string) string { ref, err := reference.ParseNormalizedNamed(image) if err != nil { return image } return reference.FamiliarName(ref) }