github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/pkg/plugins/plugin_test.go (about)

     1  package plugins // import "github.com/Prakhar-Agarwal-byte/moby/pkg/plugins"
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/Prakhar-Agarwal-byte/moby/pkg/plugins/transport"
    16  	"github.com/docker/go-connections/tlsconfig"
    17  	"github.com/pkg/errors"
    18  	"gotest.tools/v3/assert"
    19  	is "gotest.tools/v3/assert/cmp"
    20  )
    21  
    22  const (
    23  	fruitPlugin     = "fruit"
    24  	fruitImplements = "apple"
    25  )
    26  
    27  // regression test for deadlock in handlers
    28  func TestPluginAddHandler(t *testing.T) {
    29  	t.Parallel()
    30  	// make a plugin which is pre-activated
    31  	p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
    32  	p.Manifest = &Manifest{Implements: []string{"bananas"}}
    33  	storage.Lock()
    34  	storage.plugins["qwerty"] = p
    35  	storage.Unlock()
    36  
    37  	testActive(t, p)
    38  	Handle("bananas", func(_ string, _ *Client) {})
    39  	testActive(t, p)
    40  }
    41  
    42  func TestPluginWaitBadPlugin(t *testing.T) {
    43  	p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
    44  	p.activateErr = errors.New("some junk happened")
    45  	testActive(t, p)
    46  }
    47  
    48  func testActive(t *testing.T, p *Plugin) {
    49  	done := make(chan struct{})
    50  	go func() {
    51  		p.waitActive()
    52  		close(done)
    53  	}()
    54  
    55  	select {
    56  	case <-time.After(100 * time.Millisecond):
    57  		_, f, l, _ := runtime.Caller(1)
    58  		t.Fatalf("%s:%d: deadlock in waitActive", filepath.Base(f), l)
    59  	case <-done:
    60  	}
    61  }
    62  
    63  func TestGet(t *testing.T) {
    64  	// TODO: t.Parallel()
    65  	// TestPluginWithNoManifest also registers fruitPlugin
    66  
    67  	p := &Plugin{name: fruitPlugin, activateWait: sync.NewCond(&sync.Mutex{})}
    68  	p.Manifest = &Manifest{Implements: []string{fruitImplements}}
    69  	storage.Lock()
    70  	storage.plugins[fruitPlugin] = p
    71  	storage.Unlock()
    72  
    73  	t.Run("success", func(t *testing.T) {
    74  		plugin, err := Get(fruitPlugin, fruitImplements)
    75  		assert.NilError(t, err)
    76  
    77  		assert.Check(t, is.Equal(p.Name(), plugin.Name()))
    78  		assert.Check(t, is.Nil(plugin.Client()))
    79  		assert.Check(t, plugin.IsV1())
    80  	})
    81  
    82  	// check negative case where plugin fruit doesn't implement banana
    83  	t.Run("not implemented", func(t *testing.T) {
    84  		_, err := Get("fruit", "banana")
    85  		assert.Check(t, is.ErrorIs(err, ErrNotImplements))
    86  	})
    87  
    88  	// check negative case where plugin vegetable doesn't exist
    89  	t.Run("not exists", func(t *testing.T) {
    90  		_, err := Get(testNonExistingPlugin, "no-such-implementation")
    91  		assert.Check(t, is.ErrorIs(err, ErrNotFound))
    92  	})
    93  }
    94  
    95  func TestPluginWithNoManifest(t *testing.T) {
    96  	// TODO: t.Parallel()
    97  	// TestGet also registers fruitPlugin
    98  	mux, addr := setupRemotePluginServer(t)
    99  
   100  	m := Manifest{[]string{fruitImplements}}
   101  	var buf bytes.Buffer
   102  	err := json.NewEncoder(&buf).Encode(m)
   103  	assert.NilError(t, err)
   104  
   105  	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
   106  		assert.Assert(t, is.Equal(r.Method, http.MethodPost))
   107  
   108  		header := w.Header()
   109  		header.Set("Content-Type", transport.VersionMimetype)
   110  
   111  		io.Copy(w, &buf)
   112  	})
   113  
   114  	p := &Plugin{
   115  		name:         fruitPlugin,
   116  		activateWait: sync.NewCond(&sync.Mutex{}),
   117  		Addr:         addr,
   118  		TLSConfig:    &tlsconfig.Options{InsecureSkipVerify: true},
   119  	}
   120  	storage.Lock()
   121  	storage.plugins[fruitPlugin] = p
   122  	storage.Unlock()
   123  
   124  	plugin, err := Get(fruitPlugin, fruitImplements)
   125  	assert.NilError(t, err)
   126  	assert.Check(t, is.Equal(p.name, plugin.Name()))
   127  }
   128  
   129  func TestGetAll(t *testing.T) {
   130  	t.Parallel()
   131  
   132  	tmpdir := t.TempDir()
   133  	r := LocalRegistry{
   134  		socketsPath: tmpdir,
   135  		specsPaths:  []string{tmpdir},
   136  	}
   137  
   138  	p := filepath.Join(tmpdir, "example.json")
   139  	spec := `{
   140  	"Name": "example",
   141  	"Addr": "https://example.com/docker/plugin"
   142  }`
   143  
   144  	err := os.WriteFile(p, []byte(spec), 0o644)
   145  	assert.NilError(t, err)
   146  
   147  	plugin, err := r.Plugin("example")
   148  	assert.NilError(t, err)
   149  
   150  	plugin.Manifest = &Manifest{Implements: []string{"apple"}}
   151  	storage.Lock()
   152  	storage.plugins["example"] = plugin
   153  	storage.Unlock()
   154  
   155  	fetchedPlugins, err := r.GetAll("apple")
   156  	assert.NilError(t, err)
   157  	assert.Check(t, is.Len(fetchedPlugins, 1))
   158  	assert.Check(t, is.Equal(fetchedPlugins[0].Name(), plugin.Name()))
   159  }