@@ -48,18 +48,101 @@ type EventOut struct {
48
48
type Status string
49
49
50
50
const (
51
- START Status = "START "
52
- UNKNOWN Status = "UNKNOWN "
51
+ START Status = "start "
52
+ UNKNOWN Status = "unknown "
53
53
)
54
54
55
+ var statuses = [... ]Status {START , UNKNOWN }
56
+
57
+ func isStatus (status string ) bool {
58
+ status = strings .ToLower (status )
59
+
60
+ for _ , supportedStatus := range statuses {
61
+ if string (supportedStatus ) == status {
62
+ return true
63
+ }
64
+ }
65
+
66
+ return false
67
+ }
68
+
55
69
func TopicToStatus (topic string ) Status {
56
- if strings .Contains (strings .ToUpper (topic ), string (START )) {
70
+ if strings .Contains (strings .ToLower (topic ), string (START )) {
57
71
return START
58
72
}
59
73
60
74
return UNKNOWN
61
75
}
62
76
77
+ // EventFilter for filtering events
78
+ type EventFilter func (* EventOut ) bool
79
+
80
+ // generateEventFilter is similar to Podman implementation:
81
+ // https://github.com/containers/podman/blob/189d862d54b3824c74bf7474ddfed6de69ec5a09/libpod/events/filters.go#L11
82
+ func generateEventFilter (filter , filterValue string ) (func (e * EventOut ) bool , error ) {
83
+ switch strings .ToUpper (filter ) {
84
+ case "EVENT" , "STATUS" :
85
+ return func (e * EventOut ) bool {
86
+ if ! isStatus (string (e .Status )) {
87
+ return false
88
+ }
89
+
90
+ return strings .EqualFold (string (e .Status ), filterValue )
91
+ }, nil
92
+ }
93
+
94
+ return nil , fmt .Errorf ("%s is an invalid or unsupported filter" , filter )
95
+ }
96
+
97
+ // parseFilter is similar to Podman implementation:
98
+ // https://github.com/containers/podman/blob/189d862d54b3824c74bf7474ddfed6de69ec5a09/libpod/events/filters.go#L96
99
+ func parseFilter (filter string ) (string , string , error ) {
100
+ filterSplit := strings .SplitN (filter , "=" , 2 )
101
+ if len (filterSplit ) != 2 {
102
+ return "" , "" , fmt .Errorf ("%s is an invalid filter" , filter )
103
+ }
104
+ return filterSplit [0 ], filterSplit [1 ], nil
105
+ }
106
+
107
+ // applyFilters is similar to Podman implementation:
108
+ // https://github.com/containers/podman/blob/189d862d54b3824c74bf7474ddfed6de69ec5a09/libpod/events/filters.go#L106
109
+ func applyFilters (event * EventOut , filterMap map [string ][]EventFilter ) bool {
110
+ for _ , filters := range filterMap {
111
+ match := false
112
+ for _ , filter := range filters {
113
+ if filter (event ) {
114
+ match = true
115
+ break
116
+ }
117
+ }
118
+ if ! match {
119
+ return false
120
+ }
121
+ }
122
+ return true
123
+ }
124
+
125
+ // generateEventFilters is similar to Podman implementation:
126
+ // https://github.com/containers/podman/blob/189d862d54b3824c74bf7474ddfed6de69ec5a09/libpod/events/filters.go#L11
127
+ func generateEventFilters (filters []string ) (map [string ][]EventFilter , error ) {
128
+ filterMap := make (map [string ][]EventFilter )
129
+ for _ , filter := range filters {
130
+ key , val , err := parseFilter (filter )
131
+ if err != nil {
132
+ return nil , err
133
+ }
134
+ filterFunc , err := generateEventFilter (key , val )
135
+ if err != nil {
136
+ return nil , err
137
+ }
138
+ filterSlice := filterMap [key ]
139
+ filterSlice = append (filterSlice , filterFunc )
140
+ filterMap [key ] = filterSlice
141
+ }
142
+
143
+ return filterMap , nil
144
+ }
145
+
63
146
// Events is from https://github.com/containerd/containerd/blob/v1.4.3/cmd/ctr/commands/events/events.go
64
147
func Events (ctx context.Context , client * containerd.Client , options types.SystemEventsOptions ) error {
65
148
eventsClient := client .EventService ()
@@ -77,6 +160,10 @@ func Events(ctx context.Context, client *containerd.Client, options types.System
77
160
return err
78
161
}
79
162
}
163
+ filterMap , err := generateEventFilters (options .Filters )
164
+ if err != nil {
165
+ return err
166
+ }
80
167
for {
81
168
var e * events.Envelope
82
169
select {
@@ -99,37 +186,41 @@ func Events(ctx context.Context, client *containerd.Client, options types.System
99
186
continue
100
187
}
101
188
}
102
- if tmpl != nil {
103
- var data map [string ]interface {}
104
- err := json .Unmarshal (out , & data )
105
- if err != nil {
106
- log .G (ctx ).WithError (err ).Warn ("cannot marshal Any into JSON" )
107
- } else {
108
- _ , ok := data ["container_id" ]
109
- if ok {
110
- id = data ["container_id" ].(string )
111
- }
189
+ var data map [string ]interface {}
190
+ err := json .Unmarshal (out , & data )
191
+ if err != nil {
192
+ log .G (ctx ).WithError (err ).Warn ("cannot marshal Any into JSON" )
193
+ } else {
194
+ _ , ok := data ["container_id" ]
195
+ if ok {
196
+ id = data ["container_id" ].(string )
112
197
}
198
+ }
113
199
114
- out := EventOut {e .Timestamp , id , e .Namespace , e .Topic , TopicToStatus (e .Topic ), string (out )}
115
- var b bytes.Buffer
116
- if err := tmpl .Execute (& b , out ); err != nil {
117
- return err
118
- }
119
- if _ , err := fmt .Fprintln (options .Stdout , b .String ()+ "\n " ); err != nil {
120
- return err
121
- }
122
- } else {
123
- if _ , err := fmt .Fprintln (
124
- options .Stdout ,
125
- e .Timestamp ,
126
- e .Namespace ,
127
- e .Topic ,
128
- string (out ),
129
- ); err != nil {
130
- return err
200
+ eOut := EventOut {e .Timestamp , id , e .Namespace , e .Topic , TopicToStatus (e .Topic ), string (out )}
201
+ match := applyFilters (& eOut , filterMap )
202
+ if match {
203
+ if tmpl != nil {
204
+ var b bytes.Buffer
205
+ if err := tmpl .Execute (& b , eOut ); err != nil {
206
+ return err
207
+ }
208
+ if _ , err := fmt .Fprintln (options .Stdout , b .String ()+ "\n " ); err != nil {
209
+ return err
210
+ }
211
+ } else {
212
+ if _ , err := fmt .Fprintln (
213
+ options .Stdout ,
214
+ e .Timestamp ,
215
+ e .Namespace ,
216
+ e .Topic ,
217
+ string (out ),
218
+ ); err != nil {
219
+ return err
220
+ }
131
221
}
132
222
}
223
+
133
224
}
134
225
}
135
226
}
0 commit comments