github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/daemon/cluster/controllers/plugin/controller_test.go (about)

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