123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- package plugins // import "github.com/docker/docker/pkg/plugins"
- import (
- "bytes"
- "encoding/json"
- "io"
- "net/http"
- "os"
- "path/filepath"
- "runtime"
- "sync"
- "testing"
- "time"
- "github.com/docker/docker/pkg/plugins/transport"
- "github.com/docker/go-connections/tlsconfig"
- "github.com/pkg/errors"
- "gotest.tools/v3/assert"
- )
- const (
- fruitPlugin = "fruit"
- fruitImplements = "apple"
- )
- // regression test for deadlock in handlers
- func TestPluginAddHandler(t *testing.T) {
- t.Parallel()
- // make a plugin which is pre-activated
- p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
- p.Manifest = &Manifest{Implements: []string{"bananas"}}
- storage.Lock()
- storage.plugins["qwerty"] = p
- storage.Unlock()
- testActive(t, p)
- Handle("bananas", func(_ string, _ *Client) {})
- testActive(t, p)
- }
- func TestPluginWaitBadPlugin(t *testing.T) {
- p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
- p.activateErr = errors.New("some junk happened")
- testActive(t, p)
- }
- func testActive(t *testing.T, p *Plugin) {
- done := make(chan struct{})
- go func() {
- p.waitActive()
- close(done)
- }()
- select {
- case <-time.After(100 * time.Millisecond):
- _, f, l, _ := runtime.Caller(1)
- t.Fatalf("%s:%d: deadlock in waitActive", filepath.Base(f), l)
- case <-done:
- }
- }
- func TestGet(t *testing.T) {
- // TODO: t.Parallel()
- // TestPluginWithNoManifest also registers fruitPlugin
- p := &Plugin{name: fruitPlugin, activateWait: sync.NewCond(&sync.Mutex{})}
- p.Manifest = &Manifest{Implements: []string{fruitImplements}}
- storage.Lock()
- storage.plugins[fruitPlugin] = p
- storage.Unlock()
- t.Run("success", func(t *testing.T) {
- plugin, err := Get(fruitPlugin, fruitImplements)
- if err != nil {
- t.Fatal(err)
- }
- if p.Name() != plugin.Name() {
- t.Errorf("no matching plugin with name %s found", plugin.Name())
- }
- if plugin.Client() != nil {
- t.Error("expected nil Client but found one")
- }
- if !plugin.IsV1() {
- t.Error("Expected true for V1 plugin")
- }
- })
- // check negative case where plugin fruit doesn't implement banana
- t.Run("not implemented", func(t *testing.T) {
- _, err := Get("fruit", "banana")
- assert.Assert(t, errors.Is(err, ErrNotImplements))
- })
- // check negative case where plugin vegetable doesn't exist
- t.Run("not exists", func(t *testing.T) {
- _, err := Get(testNonExistingPlugin, "no-such-implementation")
- assert.Assert(t, errors.Is(err, ErrNotFound))
- })
- }
- func TestPluginWithNoManifest(t *testing.T) {
- // TODO: t.Parallel()
- // TestGet also registers fruitPlugin
- mux, addr := setupRemotePluginServer(t)
- m := Manifest{[]string{fruitImplements}}
- var buf bytes.Buffer
- if err := json.NewEncoder(&buf).Encode(m); err != nil {
- t.Fatal(err)
- }
- mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- t.Fatalf("Expected POST, got %s\n", r.Method)
- }
- header := w.Header()
- header.Set("Content-Type", transport.VersionMimetype)
- io.Copy(w, &buf)
- })
- p := &Plugin{
- name: fruitPlugin,
- activateWait: sync.NewCond(&sync.Mutex{}),
- Addr: addr,
- TLSConfig: &tlsconfig.Options{InsecureSkipVerify: true},
- }
- storage.Lock()
- storage.plugins[fruitPlugin] = p
- storage.Unlock()
- plugin, err := Get(fruitPlugin, fruitImplements)
- if err != nil {
- t.Fatal(err)
- }
- if p.Name() != plugin.Name() {
- t.Fatalf("No matching plugin with name %s found", plugin.Name())
- }
- }
- func TestGetAll(t *testing.T) {
- t.Parallel()
- tmpdir := t.TempDir()
- r := LocalRegistry{
- socketsPath: tmpdir,
- specsPaths: []string{tmpdir},
- }
- p := filepath.Join(tmpdir, "example.json")
- spec := `{
- "Name": "example",
- "Addr": "https://example.com/docker/plugin"
- }`
- if err := os.WriteFile(p, []byte(spec), 0o644); err != nil {
- t.Fatal(err)
- }
- plugin, err := r.Plugin("example")
- if err != nil {
- t.Fatal(err)
- }
- plugin.Manifest = &Manifest{Implements: []string{"apple"}}
- storage.Lock()
- storage.plugins["example"] = plugin
- storage.Unlock()
- fetchedPlugins, err := r.GetAll("apple")
- if err != nil {
- t.Fatal(err)
- }
- if fetchedPlugins[0].Name() != plugin.Name() {
- t.Fatalf("Expected to get plugin with name %s", plugin.Name())
- }
- }
|