1package events
2
3import (
4	"github.com/docker/distribution/reference"
5	"github.com/docker/docker/api/types/events"
6	"github.com/docker/docker/api/types/filters"
7)
8
9// Filter can filter out docker events from a stream
10type Filter struct {
11	filter filters.Args
12}
13
14// NewFilter creates a new Filter
15func NewFilter(filter filters.Args) *Filter {
16	return &Filter{filter: filter}
17}
18
19// Include returns true when the event ev is included by the filters
20func (ef *Filter) Include(ev events.Message) bool {
21	return ef.matchEvent(ev) &&
22		ef.filter.ExactMatch("type", ev.Type) &&
23		ef.matchScope(ev.Scope) &&
24		ef.matchDaemon(ev) &&
25		ef.matchContainer(ev) &&
26		ef.matchPlugin(ev) &&
27		ef.matchVolume(ev) &&
28		ef.matchNetwork(ev) &&
29		ef.matchImage(ev) &&
30		ef.matchNode(ev) &&
31		ef.matchService(ev) &&
32		ef.matchSecret(ev) &&
33		ef.matchConfig(ev) &&
34		ef.matchLabels(ev.Actor.Attributes)
35}
36
37func (ef *Filter) matchEvent(ev events.Message) bool {
38	// #25798 if an event filter contains either health_status, exec_create or exec_start without a colon
39	// Let's to a FuzzyMatch instead of an ExactMatch.
40	if ef.filterContains("event", map[string]struct{}{"health_status": {}, "exec_create": {}, "exec_start": {}}) {
41		return ef.filter.FuzzyMatch("event", ev.Action)
42	}
43	return ef.filter.ExactMatch("event", ev.Action)
44}
45
46func (ef *Filter) filterContains(field string, values map[string]struct{}) bool {
47	for _, v := range ef.filter.Get(field) {
48		if _, ok := values[v]; ok {
49			return true
50		}
51	}
52	return false
53}
54
55func (ef *Filter) matchScope(scope string) bool {
56	if !ef.filter.Contains("scope") {
57		return true
58	}
59	return ef.filter.ExactMatch("scope", scope)
60}
61
62func (ef *Filter) matchLabels(attributes map[string]string) bool {
63	if !ef.filter.Contains("label") {
64		return true
65	}
66	return ef.filter.MatchKVList("label", attributes)
67}
68
69func (ef *Filter) matchDaemon(ev events.Message) bool {
70	return ef.fuzzyMatchName(ev, events.DaemonEventType)
71}
72
73func (ef *Filter) matchContainer(ev events.Message) bool {
74	return ef.fuzzyMatchName(ev, events.ContainerEventType)
75}
76
77func (ef *Filter) matchPlugin(ev events.Message) bool {
78	return ef.fuzzyMatchName(ev, events.PluginEventType)
79}
80
81func (ef *Filter) matchVolume(ev events.Message) bool {
82	return ef.fuzzyMatchName(ev, events.VolumeEventType)
83}
84
85func (ef *Filter) matchNetwork(ev events.Message) bool {
86	return ef.fuzzyMatchName(ev, events.NetworkEventType)
87}
88
89func (ef *Filter) matchService(ev events.Message) bool {
90	return ef.fuzzyMatchName(ev, events.ServiceEventType)
91}
92
93func (ef *Filter) matchNode(ev events.Message) bool {
94	return ef.fuzzyMatchName(ev, events.NodeEventType)
95}
96
97func (ef *Filter) matchSecret(ev events.Message) bool {
98	return ef.fuzzyMatchName(ev, events.SecretEventType)
99}
100
101func (ef *Filter) matchConfig(ev events.Message) bool {
102	return ef.fuzzyMatchName(ev, events.ConfigEventType)
103}
104
105func (ef *Filter) fuzzyMatchName(ev events.Message, eventType string) bool {
106	return ef.filter.FuzzyMatch(eventType, ev.Actor.ID) ||
107		ef.filter.FuzzyMatch(eventType, ev.Actor.Attributes["name"])
108}
109
110// matchImage matches against both event.Actor.ID (for image events)
111// and event.Actor.Attributes["image"] (for container events), so that any container that was created
112// from an image will be included in the image events. Also compare both
113// against the stripped repo name without any tags.
114func (ef *Filter) matchImage(ev events.Message) bool {
115	id := ev.Actor.ID
116	nameAttr := "image"
117	var imageName string
118
119	if ev.Type == events.ImageEventType {
120		nameAttr = "name"
121	}
122
123	if n, ok := ev.Actor.Attributes[nameAttr]; ok {
124		imageName = n
125	}
126	return ef.filter.ExactMatch("image", id) ||
127		ef.filter.ExactMatch("image", imageName) ||
128		ef.filter.ExactMatch("image", stripTag(id)) ||
129		ef.filter.ExactMatch("image", stripTag(imageName))
130}
131
132func stripTag(image string) string {
133	ref, err := reference.ParseNormalizedNamed(image)
134	if err != nil {
135		return image
136	}
137	return reference.FamiliarName(ref)
138}
139