
In the course of setting out to add filters and address some cleanup, it was found that we had a few problems in the events subsystem that needed addressing before moving forward. The biggest change was to move to the more standard terminology of publish and subscribe. We make this terminology change across the Go interface and the GRPC API, making the behavior more familier. The previous system was very context-oriented, which is no longer required. With this, we've removed a large amount of dead and unneeded code. Event transactions, context storage and the concept of `Poster` is gone. This has been replaced in most places with a `Publisher`, which matches the actual usage throughout the codebase, removing the need for helpers. There are still some questions around the way events are handled in the shim. Right now, we've preserved some of the existing bugs which may require more extensive changes to resolve correctly. Signed-off-by: Stephen J Day <stephen.day@docker.com>
150 lines
3.2 KiB
Go
150 lines
3.2 KiB
Go
package events
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"sync"
|
|
"testing"
|
|
|
|
events "github.com/containerd/containerd/api/services/events/v1"
|
|
"github.com/containerd/containerd/errdefs"
|
|
"github.com/containerd/containerd/namespaces"
|
|
"github.com/containerd/containerd/typeurl"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func TestExchangeBasic(t *testing.T) {
|
|
ctx := namespaces.WithNamespace(context.Background(), t.Name())
|
|
testevents := []Event{
|
|
&events.ContainerCreate{ID: "asdf"},
|
|
&events.ContainerCreate{ID: "qwer"},
|
|
&events.ContainerCreate{ID: "zxcv"},
|
|
}
|
|
exchange := NewExchange()
|
|
|
|
t.Log("subscribe")
|
|
var cancel1, cancel2 func()
|
|
|
|
// Create two subscribers for same set of events and make sure they
|
|
// traverse the exchange.
|
|
ctx1, cancel1 := context.WithCancel(ctx)
|
|
eventq1, errq1 := exchange.Subscribe(ctx1)
|
|
|
|
ctx2, cancel2 := context.WithCancel(ctx)
|
|
eventq2, errq2 := exchange.Subscribe(ctx2)
|
|
|
|
t.Log("publish")
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for _, event := range testevents {
|
|
fmt.Println("publish", event)
|
|
if err := exchange.Publish(ctx, "/test", event); err != nil {
|
|
fmt.Println("publish error", err)
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
t.Log("finished publishing")
|
|
}()
|
|
|
|
t.Log("waiting")
|
|
wg.Wait()
|
|
|
|
for _, subscriber := range []struct {
|
|
eventq <-chan *events.Envelope
|
|
errq <-chan error
|
|
cancel func()
|
|
}{
|
|
{
|
|
eventq: eventq1,
|
|
errq: errq1,
|
|
cancel: cancel1,
|
|
},
|
|
{
|
|
eventq: eventq2,
|
|
errq: errq2,
|
|
cancel: cancel2,
|
|
},
|
|
} {
|
|
var received []Event
|
|
subscribercheck:
|
|
for {
|
|
select {
|
|
case env := <-subscriber.eventq:
|
|
ev, err := typeurl.UnmarshalAny(env.Event)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
received = append(received, ev.(*events.ContainerCreate))
|
|
case err := <-subscriber.errq:
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
break subscribercheck
|
|
}
|
|
|
|
if reflect.DeepEqual(received, testevents) {
|
|
// when we do this, we expect the errs channel to be closed and
|
|
// this will return.
|
|
subscriber.cancel()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExchangeValidateTopic(t *testing.T) {
|
|
namespace := t.Name()
|
|
ctx := namespaces.WithNamespace(context.Background(), namespace)
|
|
exchange := NewExchange()
|
|
|
|
for _, testcase := range []struct {
|
|
input string
|
|
err error
|
|
}{
|
|
{
|
|
input: "/test",
|
|
},
|
|
{
|
|
input: "/test/test",
|
|
},
|
|
{
|
|
input: "test",
|
|
err: errdefs.ErrInvalidArgument,
|
|
},
|
|
} {
|
|
t.Run(testcase.input, func(t *testing.T) {
|
|
event := &events.ContainerCreate{ID: t.Name()}
|
|
if err := exchange.Publish(ctx, testcase.input, event); errors.Cause(err) != testcase.err {
|
|
if err == nil {
|
|
t.Fatalf("expected error %v, received nil", testcase.err)
|
|
} else {
|
|
t.Fatalf("expected error %v, received %v", testcase.err, err)
|
|
}
|
|
}
|
|
|
|
evany, err := typeurl.MarshalAny(event)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
envelope := events.Envelope{
|
|
Namespace: namespace,
|
|
Topic: testcase.input,
|
|
Event: evany,
|
|
}
|
|
// make sure we get same errors with forward.
|
|
if err := exchange.Forward(ctx, &envelope); errors.Cause(err) != testcase.err {
|
|
if err == nil {
|
|
t.Fatalf("expected error %v, received nil", testcase.err)
|
|
} else {
|
|
t.Fatalf("expected error %v, received %v", testcase.err, err)
|
|
}
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|