github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/cluster/controllers/plugin/controller_test.go (about)

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