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