github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/cluster/controllers/plugin/controller_test.go (about)

     1  package plugin // import "github.com/demonoid81/moby/daemon/cluster/controllers/plugin"
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/docker/distribution/reference"
    14  	enginetypes "github.com/demonoid81/moby/api/types"
    15  	"github.com/demonoid81/moby/api/types/swarm/runtime"
    16  	"github.com/demonoid81/moby/pkg/pubsub"
    17  	"github.com/demonoid81/moby/plugin"
    18  	v2 "github.com/demonoid81/moby/plugin/v2"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  const (
    23  	pluginTestName          = "test"
    24  	pluginTestRemote        = "testremote"
    25  	pluginTestRemoteUpgrade = "testremote2"
    26  )
    27  
    28  func TestPrepare(t *testing.T) {
    29  	b := newMockBackend()
    30  	c := newTestController(b, false)
    31  	ctx := context.Background()
    32  
    33  	if err := c.Prepare(ctx); err != nil {
    34  		t.Fatal(err)
    35  	}
    36  
    37  	if b.p == nil {
    38  		t.Fatal("pull not performed")
    39  	}
    40  
    41  	c = newTestController(b, false)
    42  	if err := c.Prepare(ctx); err != nil {
    43  		t.Fatal(err)
    44  	}
    45  	if b.p == nil {
    46  		t.Fatal("unexpected nil")
    47  	}
    48  	if b.p.PluginObj.PluginReference != pluginTestRemoteUpgrade {
    49  		t.Fatal("upgrade not performed")
    50  	}
    51  
    52  	c = newTestController(b, false)
    53  	c.serviceID = "1"
    54  	if err := c.Prepare(ctx); err == nil {
    55  		t.Fatal("expected error on prepare")
    56  	}
    57  }
    58  
    59  func TestStart(t *testing.T) {
    60  	b := newMockBackend()
    61  	c := newTestController(b, false)
    62  	ctx := context.Background()
    63  
    64  	if err := c.Prepare(ctx); err != nil {
    65  		t.Fatal(err)
    66  	}
    67  
    68  	if err := c.Start(ctx); err != nil {
    69  		t.Fatal(err)
    70  	}
    71  
    72  	if !b.p.IsEnabled() {
    73  		t.Fatal("expected plugin to be enabled")
    74  	}
    75  
    76  	c = newTestController(b, true)
    77  	if err := c.Prepare(ctx); err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	if err := c.Start(ctx); err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	if b.p.IsEnabled() {
    84  		t.Fatal("expected plugin to be disabled")
    85  	}
    86  
    87  	c = newTestController(b, false)
    88  	if err := c.Prepare(ctx); err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	if err := c.Start(ctx); err != nil {
    92  		t.Fatal(err)
    93  	}
    94  	if !b.p.IsEnabled() {
    95  		t.Fatal("expected plugin to be enabled")
    96  	}
    97  }
    98  
    99  func TestWaitCancel(t *testing.T) {
   100  	b := newMockBackend()
   101  	c := newTestController(b, true)
   102  	ctx := context.Background()
   103  	if err := c.Prepare(ctx); err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	if err := c.Start(ctx); err != nil {
   107  		t.Fatal(err)
   108  	}
   109  
   110  	ctxCancel, cancel := context.WithCancel(ctx)
   111  	chErr := make(chan error)
   112  	go func() {
   113  		chErr <- c.Wait(ctxCancel)
   114  	}()
   115  	cancel()
   116  	select {
   117  	case err := <-chErr:
   118  		if err != context.Canceled {
   119  			t.Fatal(err)
   120  		}
   121  	case <-time.After(10 * time.Second):
   122  		t.Fatal("timeout waiting for cancelation")
   123  	}
   124  }
   125  
   126  func TestWaitDisabled(t *testing.T) {
   127  	b := newMockBackend()
   128  	c := newTestController(b, true)
   129  	ctx := context.Background()
   130  	if err := c.Prepare(ctx); err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	if err := c.Start(ctx); err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	chErr := make(chan error)
   138  	go func() {
   139  		chErr <- c.Wait(ctx)
   140  	}()
   141  
   142  	if err := b.Enable("test", nil); err != nil {
   143  		t.Fatal(err)
   144  	}
   145  	select {
   146  	case err := <-chErr:
   147  		if err == nil {
   148  			t.Fatal("expected error")
   149  		}
   150  	case <-time.After(10 * time.Second):
   151  		t.Fatal("timeout waiting for event")
   152  	}
   153  
   154  	if err := c.Start(ctx); err != nil {
   155  		t.Fatal(err)
   156  	}
   157  
   158  	ctxWaitReady, cancelCtxWaitReady := context.WithTimeout(ctx, 30*time.Second)
   159  	c.signalWaitReady = cancelCtxWaitReady
   160  	defer cancelCtxWaitReady()
   161  
   162  	go func() {
   163  		chErr <- c.Wait(ctx)
   164  	}()
   165  
   166  	chEvent, cancel := b.SubscribeEvents(1)
   167  	defer cancel()
   168  
   169  	if err := b.Disable("test", nil); err != nil {
   170  		t.Fatal(err)
   171  	}
   172  
   173  	select {
   174  	case <-chEvent:
   175  		<-ctxWaitReady.Done()
   176  		if err := ctxWaitReady.Err(); err == context.DeadlineExceeded {
   177  			t.Fatal(err)
   178  		}
   179  		select {
   180  		case <-chErr:
   181  			t.Fatal("wait returned unexpectedly")
   182  		default:
   183  			// all good
   184  		}
   185  	case <-chErr:
   186  		t.Fatal("wait returned unexpectedly")
   187  	case <-time.After(10 * time.Second):
   188  		t.Fatal("timeout waiting for event")
   189  	}
   190  
   191  	if err := b.Remove("test", nil); err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	select {
   195  	case err := <-chErr:
   196  		if err == nil {
   197  			t.Fatal("expected error")
   198  		}
   199  		if !strings.Contains(err.Error(), "removed") {
   200  			t.Fatal(err)
   201  		}
   202  	case <-time.After(10 * time.Second):
   203  		t.Fatal("timeout waiting for event")
   204  	}
   205  }
   206  
   207  func TestWaitEnabled(t *testing.T) {
   208  	b := newMockBackend()
   209  	c := newTestController(b, false)
   210  	ctx := context.Background()
   211  	if err := c.Prepare(ctx); err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	if err := c.Start(ctx); err != nil {
   215  		t.Fatal(err)
   216  	}
   217  
   218  	chErr := make(chan error)
   219  	go func() {
   220  		chErr <- c.Wait(ctx)
   221  	}()
   222  
   223  	if err := b.Disable("test", nil); err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	select {
   227  	case err := <-chErr:
   228  		if err == nil {
   229  			t.Fatal("expected error")
   230  		}
   231  	case <-time.After(10 * time.Second):
   232  		t.Fatal("timeout waiting for event")
   233  	}
   234  
   235  	if err := c.Start(ctx); err != nil {
   236  		t.Fatal(err)
   237  	}
   238  
   239  	ctxWaitReady, ctxWaitCancel := context.WithCancel(ctx)
   240  	c.signalWaitReady = ctxWaitCancel
   241  	defer ctxWaitCancel()
   242  
   243  	go func() {
   244  		chErr <- c.Wait(ctx)
   245  	}()
   246  
   247  	chEvent, cancel := b.SubscribeEvents(1)
   248  	defer cancel()
   249  
   250  	if err := b.Enable("test", nil); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  
   254  	select {
   255  	case <-chEvent:
   256  		<-ctxWaitReady.Done()
   257  		if err := ctxWaitReady.Err(); err == context.DeadlineExceeded {
   258  			t.Fatal(err)
   259  		}
   260  		select {
   261  		case <-chErr:
   262  			t.Fatal("wait returned unexpectedly")
   263  		default:
   264  			// all good
   265  		}
   266  	case <-chErr:
   267  		t.Fatal("wait returned unexpectedly")
   268  	case <-time.After(10 * time.Second):
   269  		t.Fatal("timeout waiting for event")
   270  	}
   271  
   272  	if err := b.Remove("test", nil); err != nil {
   273  		t.Fatal(err)
   274  	}
   275  	select {
   276  	case err := <-chErr:
   277  		if err == nil {
   278  			t.Fatal("expected error")
   279  		}
   280  		if !strings.Contains(err.Error(), "removed") {
   281  			t.Fatal(err)
   282  		}
   283  	case <-time.After(10 * time.Second):
   284  		t.Fatal("timeout waiting for event")
   285  	}
   286  }
   287  
   288  func TestRemove(t *testing.T) {
   289  	b := newMockBackend()
   290  	c := newTestController(b, false)
   291  	ctx := context.Background()
   292  
   293  	if err := c.Prepare(ctx); err != nil {
   294  		t.Fatal(err)
   295  	}
   296  	if err := c.Shutdown(ctx); err != nil {
   297  		t.Fatal(err)
   298  	}
   299  
   300  	c2 := newTestController(b, false)
   301  	if err := c2.Prepare(ctx); err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	if err := c.Remove(ctx); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	if b.p == nil {
   309  		t.Fatal("plugin removed unexpectedly")
   310  	}
   311  	if err := c2.Shutdown(ctx); err != nil {
   312  		t.Fatal(err)
   313  	}
   314  	if err := c2.Remove(ctx); err != nil {
   315  		t.Fatal(err)
   316  	}
   317  	if b.p != nil {
   318  		t.Fatal("expected plugin to be removed")
   319  	}
   320  }
   321  
   322  func newTestController(b Backend, disabled bool) *Controller {
   323  	return &Controller{
   324  		logger:  &logrus.Entry{Logger: &logrus.Logger{Out: ioutil.Discard}},
   325  		backend: b,
   326  		spec: runtime.PluginSpec{
   327  			Name:     pluginTestName,
   328  			Remote:   pluginTestRemote,
   329  			Disabled: disabled,
   330  		},
   331  	}
   332  }
   333  
   334  func newMockBackend() *mockBackend {
   335  	return &mockBackend{
   336  		pub: pubsub.NewPublisher(0, 0),
   337  	}
   338  }
   339  
   340  type mockBackend struct {
   341  	p   *v2.Plugin
   342  	pub *pubsub.Publisher
   343  }
   344  
   345  func (m *mockBackend) Disable(name string, config *enginetypes.PluginDisableConfig) error {
   346  	m.p.PluginObj.Enabled = false
   347  	m.pub.Publish(plugin.EventDisable{})
   348  	return nil
   349  }
   350  
   351  func (m *mockBackend) Enable(name string, config *enginetypes.PluginEnableConfig) error {
   352  	m.p.PluginObj.Enabled = true
   353  	m.pub.Publish(plugin.EventEnable{})
   354  	return nil
   355  }
   356  
   357  func (m *mockBackend) Remove(name string, config *enginetypes.PluginRmConfig) error {
   358  	m.p = nil
   359  	m.pub.Publish(plugin.EventRemove{})
   360  	return nil
   361  }
   362  
   363  func (m *mockBackend) Pull(ctx context.Context, ref reference.Named, name string, metaHeaders http.Header, authConfig *enginetypes.AuthConfig, privileges enginetypes.PluginPrivileges, outStream io.Writer, opts ...plugin.CreateOpt) error {
   364  	m.p = &v2.Plugin{
   365  		PluginObj: enginetypes.Plugin{
   366  			ID:              "1234",
   367  			Name:            name,
   368  			PluginReference: ref.String(),
   369  		},
   370  	}
   371  	return nil
   372  }
   373  
   374  func (m *mockBackend) Upgrade(ctx context.Context, ref reference.Named, name string, metaHeaders http.Header, authConfig *enginetypes.AuthConfig, privileges enginetypes.PluginPrivileges, outStream io.Writer) error {
   375  	m.p.PluginObj.PluginReference = pluginTestRemoteUpgrade
   376  	return nil
   377  }
   378  
   379  func (m *mockBackend) Get(name string) (*v2.Plugin, error) {
   380  	if m.p == nil {
   381  		return nil, errors.New("not found")
   382  	}
   383  	return m.p, nil
   384  }
   385  
   386  func (m *mockBackend) SubscribeEvents(buffer int, events ...plugin.Event) (eventCh <-chan interface{}, cancel func()) {
   387  	ch := m.pub.SubscribeTopicWithBuffer(nil, buffer)
   388  	cancel = func() { m.pub.Evict(ch) }
   389  	return ch, cancel
   390  }