blob: ce98078f87c1712160fefda4b1cdd2e04e50c321 [file] [log] [blame]
package plugins // import "github.com/docker/docker/pkg/plugins"
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"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/assert"
)
const (
fruitPlugin = "fruit"
fruitImplements = "apple"
)
// regression test for deadlock in handlers
func TestPluginAddHandler(t *testing.T) {
// make a plugin which is pre-activated
p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
p.Manifest = &Manifest{Implements: []string{"bananas"}}
storage.plugins["qwerty"] = p
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) {
p := &Plugin{name: fruitPlugin, activateWait: sync.NewCond(&sync.Mutex{})}
p.Manifest = &Manifest{Implements: []string{fruitImplements}}
storage.plugins[fruitPlugin] = p
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())
}
if plugin.Client() != nil {
t.Fatal("expected nil Client but found one")
}
if !plugin.IsV1() {
t.Fatal("Expected true for V1 plugin")
}
// check negative case where plugin fruit doesn't implement banana
_, err = Get("fruit", "banana")
assert.Equal(t, errors.Cause(err), ErrNotImplements)
// check negative case where plugin vegetable doesn't exist
_, err = Get("vegetable", "potato")
assert.Equal(t, errors.Cause(err), ErrNotFound)
}
func TestPluginWithNoManifest(t *testing.T) {
addr := setupRemotePluginServer()
defer teardownRemotePluginServer()
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 != "POST" {
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.plugins[fruitPlugin] = p
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) {
tmpdir, unregister := Setup(t)
defer unregister()
p := filepath.Join(tmpdir, "example.json")
spec := `{
"Name": "example",
"Addr": "https://example.com/docker/plugin"
}`
if err := ioutil.WriteFile(p, []byte(spec), 0644); err != nil {
t.Fatal(err)
}
r := newLocalRegistry()
plugin, err := r.Plugin("example")
if err != nil {
t.Fatal(err)
}
plugin.Manifest = &Manifest{Implements: []string{"apple"}}
storage.plugins["example"] = plugin
fetchedPlugins, err := 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())
}
}