github.com/lalkh/containerd@v1.4.3/events/exchange/exchange_test.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package exchange 18 19 import ( 20 "context" 21 "reflect" 22 "testing" 23 "time" 24 25 eventstypes "github.com/containerd/containerd/api/events" 26 "github.com/containerd/containerd/errdefs" 27 "github.com/containerd/containerd/events" 28 "github.com/containerd/containerd/namespaces" 29 "github.com/containerd/typeurl" 30 "github.com/pkg/errors" 31 ) 32 33 func TestExchangeBasic(t *testing.T) { 34 ctx := namespaces.WithNamespace(context.Background(), t.Name()) 35 testevents := []events.Event{ 36 &eventstypes.ContainerCreate{ID: "asdf"}, 37 &eventstypes.ContainerCreate{ID: "qwer"}, 38 &eventstypes.ContainerCreate{ID: "zxcv"}, 39 } 40 exchange := NewExchange() 41 42 t.Log("subscribe") 43 var cancel1, cancel2 func() 44 45 // Create two subscribers for same set of events and make sure they 46 // traverse the exchange. 47 ctx1, cancel1 := context.WithCancel(ctx) 48 eventq1, errq1 := exchange.Subscribe(ctx1) 49 50 ctx2, cancel2 := context.WithCancel(ctx) 51 eventq2, errq2 := exchange.Subscribe(ctx2) 52 53 t.Log("publish") 54 errChan := make(chan error) 55 go func() { 56 defer close(errChan) 57 for _, event := range testevents { 58 if err := exchange.Publish(ctx, "/test", event); err != nil { 59 errChan <- err 60 return 61 } 62 } 63 64 t.Log("finished publishing") 65 }() 66 67 t.Log("waiting") 68 if err := <-errChan; err != nil { 69 t.Fatal(err) 70 } 71 72 for _, subscriber := range []struct { 73 eventq <-chan *events.Envelope 74 errq <-chan error 75 cancel func() 76 }{ 77 { 78 eventq: eventq1, 79 errq: errq1, 80 cancel: cancel1, 81 }, 82 { 83 eventq: eventq2, 84 errq: errq2, 85 cancel: cancel2, 86 }, 87 } { 88 var received []events.Event 89 subscribercheck: 90 for { 91 select { 92 case env := <-subscriber.eventq: 93 ev, err := typeurl.UnmarshalAny(env.Event) 94 if err != nil { 95 t.Fatal(err) 96 } 97 received = append(received, ev.(*eventstypes.ContainerCreate)) 98 case err := <-subscriber.errq: 99 if err != nil { 100 t.Fatal(err) 101 } 102 break subscribercheck 103 } 104 105 if reflect.DeepEqual(received, testevents) { 106 // when we do this, we expect the errs channel to be closed and 107 // this will return. 108 subscriber.cancel() 109 } 110 } 111 } 112 } 113 114 func TestExchangeFilters(t *testing.T) { 115 var ( 116 ctx = namespaces.WithNamespace(context.Background(), t.Name()) 117 exchange = NewExchange() 118 119 // config events, All events will be published 120 containerCreateEvents = []events.Event{ 121 &eventstypes.ContainerCreate{ID: "asdf"}, 122 &eventstypes.ContainerCreate{ID: "qwer"}, 123 &eventstypes.ContainerCreate{ID: "zxcv"}, 124 } 125 taskExitEvents = []events.Event{ 126 &eventstypes.TaskExit{ContainerID: "abcdef"}, 127 } 128 testEventSets = []struct { 129 topic string 130 events []events.Event 131 }{ 132 { 133 topic: "/containers/create", 134 events: containerCreateEvents, 135 }, 136 { 137 topic: "/tasks/exit", 138 events: taskExitEvents, 139 }, 140 } 141 allTestEvents = func(eventSets []struct { 142 topic string 143 events []events.Event 144 }) (events []events.Event) { 145 for _, v := range eventSets { 146 events = append(events, v.events...) 147 } 148 return 149 }(testEventSets) 150 151 // config test cases 152 testCases = []struct { 153 testName string 154 filters []string 155 156 // The fields as below are for store data. Don't config them. 157 expectEvents []events.Event 158 eventq <-chan *events.Envelope 159 errq <-chan error 160 cancel func() 161 }{ 162 { 163 testName: "No Filter", 164 expectEvents: allTestEvents, 165 }, 166 { 167 testName: "Filter events by one topic", 168 filters: []string{ 169 `topic=="/containers/create"`, 170 }, 171 expectEvents: containerCreateEvents, 172 }, 173 { 174 testName: "Filter events by field", 175 filters: []string{ 176 "event.id", 177 }, 178 expectEvents: containerCreateEvents, 179 }, 180 { 181 testName: "Filter events by field OR topic", 182 filters: []string{ 183 `topic=="/containers/create"`, 184 "event.id", 185 }, 186 expectEvents: containerCreateEvents, 187 }, 188 { 189 testName: "Filter events by regex ", 190 filters: []string{ 191 `topic~="/containers/*"`, 192 }, 193 expectEvents: containerCreateEvents, 194 }, 195 { 196 testName: "Filter events for by anyone of two topics", 197 filters: []string{ 198 `topic=="/tasks/exit"`, 199 `topic=="/containers/create"`, 200 }, 201 expectEvents: append(containerCreateEvents, taskExitEvents...), 202 }, 203 { 204 testName: "Filter events for by one topic AND id", 205 filters: []string{ 206 `topic=="/containers/create",event.id=="qwer"`, 207 }, 208 expectEvents: []events.Event{ 209 &eventstypes.ContainerCreate{ID: "qwer"}, 210 }, 211 }, 212 } 213 ) 214 215 t.Log("subscribe") 216 for i := range testCases { 217 var ctx1 context.Context 218 ctx1, testCases[i].cancel = context.WithCancel(ctx) 219 testCases[i].eventq, testCases[i].errq = exchange.Subscribe(ctx1, testCases[i].filters...) 220 } 221 222 t.Log("publish") 223 errChan := make(chan error) 224 go func() { 225 defer close(errChan) 226 for _, es := range testEventSets { 227 for _, e := range es.events { 228 if err := exchange.Publish(ctx, es.topic, e); err != nil { 229 errChan <- err 230 return 231 } 232 } 233 } 234 235 t.Log("finished publishing") 236 }() 237 238 t.Log("waiting") 239 if err := <-errChan; err != nil { 240 t.Fatal(err) 241 } 242 243 t.Log("receive event") 244 for _, subscriber := range testCases { 245 t.Logf("test case: %q", subscriber.testName) 246 var received []events.Event 247 subscribercheck: 248 for { 249 select { 250 case env := <-subscriber.eventq: 251 ev, err := typeurl.UnmarshalAny(env.Event) 252 if err != nil { 253 t.Fatal(err) 254 } 255 received = append(received, ev) 256 case err := <-subscriber.errq: 257 if err != nil { 258 t.Fatal(err) 259 } 260 break subscribercheck 261 } 262 263 if reflect.DeepEqual(received, subscriber.expectEvents) { 264 // when we do this, we expect the errs channel to be closed and 265 // this will return. 266 subscriber.cancel() 267 } 268 } 269 } 270 } 271 272 func TestExchangeValidateTopic(t *testing.T) { 273 namespace := t.Name() 274 ctx := namespaces.WithNamespace(context.Background(), namespace) 275 exchange := NewExchange() 276 277 for _, testcase := range []struct { 278 input string 279 err error 280 }{ 281 { 282 input: "/test", 283 }, 284 { 285 input: "/test/test", 286 }, 287 { 288 input: "test", 289 err: errdefs.ErrInvalidArgument, 290 }, 291 } { 292 t.Run(testcase.input, func(t *testing.T) { 293 event := &eventstypes.ContainerCreate{ID: t.Name()} 294 if err := exchange.Publish(ctx, testcase.input, event); !errors.Is(err, testcase.err) { 295 if err == nil { 296 t.Fatalf("expected error %v, received nil", testcase.err) 297 } else { 298 t.Fatalf("expected error %v, received %v", testcase.err, err) 299 } 300 } 301 302 evany, err := typeurl.MarshalAny(event) 303 if err != nil { 304 t.Fatal(err) 305 } 306 307 envelope := events.Envelope{ 308 Timestamp: time.Now().UTC(), 309 Namespace: namespace, 310 Topic: testcase.input, 311 Event: evany, 312 } 313 314 // make sure we get same errors with forward. 315 if err := exchange.Forward(ctx, &envelope); !errors.Is(err, testcase.err) { 316 if err == nil { 317 t.Fatalf("expected error %v, received nil", testcase.err) 318 } else { 319 t.Fatalf("expected error %v, received %v", testcase.err, err) 320 } 321 } 322 323 }) 324 } 325 }