From 8dd100a2297a34a0aef422383117fb0c3314fba1 Mon Sep 17 00:00:00 2001 From: Raja Sami Date: Mon, 22 May 2017 14:38:23 +0500 Subject: [PATCH] Increase the Coverage of pkg/plugins Increases the test coverage of pkg/plugins. Changed signature of function NewClientWithTimeout in pkg/plugin/client, to take time.Duration instead of integers. Signed-off-by: Raja Sami --- pkg/plugins/client.go | 8 +-- pkg/plugins/client_test.go | 87 ++++++++++++++++++++-- pkg/plugins/discovery_unix_test.go | 39 ++++++++++ pkg/plugins/plugin_test.go | 112 +++++++++++++++++++++++++++++ pkg/plugins/transport/http_test.go | 20 ++++++ plugin/manager_linux.go | 2 +- 6 files changed, 259 insertions(+), 9 deletions(-) create mode 100644 pkg/plugins/transport/http_test.go diff --git a/pkg/plugins/client.go b/pkg/plugins/client.go index 84103c7ae7..f221a46fcf 100644 --- a/pkg/plugins/client.go +++ b/pkg/plugins/client.go @@ -57,20 +57,20 @@ func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) { } // NewClientWithTimeout creates a new plugin client (http). -func NewClientWithTimeout(addr string, tlsConfig *tlsconfig.Options, timeoutInSecs int) (*Client, error) { +func NewClientWithTimeout(addr string, tlsConfig *tlsconfig.Options, timeout time.Duration) (*Client, error) { clientTransport, err := newTransport(addr, tlsConfig) if err != nil { return nil, err } - return newClientWithTransport(clientTransport, timeoutInSecs), nil + return newClientWithTransport(clientTransport, timeout), nil } // newClientWithTransport creates a new plugin client with a given transport. -func newClientWithTransport(tr transport.Transport, timeoutInSecs int) *Client { +func newClientWithTransport(tr transport.Transport, timeout time.Duration) *Client { return &Client{ http: &http.Client{ Transport: tr, - Timeout: time.Duration(timeoutInSecs) * time.Second, + Timeout: timeout, }, requestFactory: tr, } diff --git a/pkg/plugins/client_test.go b/pkg/plugins/client_test.go index fc583fe0c2..7c519a2765 100644 --- a/pkg/plugins/client_test.go +++ b/pkg/plugins/client_test.go @@ -1,17 +1,19 @@ package plugins import ( + "bytes" + "encoding/json" "io" "net/http" "net/http/httptest" "net/url" - "reflect" "strings" "testing" "time" "github.com/docker/docker/pkg/plugins/transport" "github.com/docker/go-connections/tlsconfig" + "github.com/stretchr/testify/assert" ) var ( @@ -83,9 +85,7 @@ func TestEchoInputOutput(t *testing.T) { t.Fatal(err) } - if !reflect.DeepEqual(output, m) { - t.Fatalf("Expected %v, was %v\n", m, output) - } + assert.Equal(t, m, output) err = c.Call("Test.Echo", nil, nil) if err != nil { t.Fatal(err) @@ -153,3 +153,82 @@ func TestClientScheme(t *testing.T) { } } } + +func TestNewClientWithTimeout(t *testing.T) { + addr := setupRemotePluginServer() + defer teardownRemotePluginServer() + + m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}} + + mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) { + time.Sleep(time.Duration(600) * time.Millisecond) + io.Copy(w, r.Body) + }) + + // setting timeout of 500ms + timeout := time.Duration(500) * time.Millisecond + c, _ := NewClientWithTimeout(addr, &tlsconfig.Options{InsecureSkipVerify: true}, timeout) + var output Manifest + err := c.Call("Test.Echo", m, &output) + if err == nil { + t.Fatal("Expected timeout error") + } +} + +func TestClientStream(t *testing.T) { + addr := setupRemotePluginServer() + defer teardownRemotePluginServer() + + m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}} + var output Manifest + + mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Fatalf("Expected POST, got %s", r.Method) + } + + header := w.Header() + header.Set("Content-Type", transport.VersionMimetype) + + io.Copy(w, r.Body) + }) + + c, _ := NewClient(addr, &tlsconfig.Options{InsecureSkipVerify: true}) + body, err := c.Stream("Test.Echo", m) + if err != nil { + t.Fatal(err) + } + defer body.Close() + if err := json.NewDecoder(body).Decode(&output); err != nil { + t.Fatalf("Test.Echo: error reading plugin resp: %v", err) + } + assert.Equal(t, m, output) +} + +func TestClientSendFile(t *testing.T) { + addr := setupRemotePluginServer() + defer teardownRemotePluginServer() + + m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}} + var output Manifest + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(m); err != nil { + t.Fatal(err) + } + mux.HandleFunc("/Test.Echo", 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, r.Body) + }) + + c, _ := NewClient(addr, &tlsconfig.Options{InsecureSkipVerify: true}) + if err := c.SendFile("Test.Echo", &buf, &output); err != nil { + t.Fatal(err) + } + assert.Equal(t, m, output) +} diff --git a/pkg/plugins/discovery_unix_test.go b/pkg/plugins/discovery_unix_test.go index 1b232b7ade..66f50353c3 100644 --- a/pkg/plugins/discovery_unix_test.go +++ b/pkg/plugins/discovery_unix_test.go @@ -4,6 +4,7 @@ package plugins import ( "fmt" + "io/ioutil" "net" "os" "path/filepath" @@ -59,3 +60,41 @@ func TestLocalSocket(t *testing.T) { l.Close() } } + +func TestScan(t *testing.T) { + tmpdir, unregister := Setup(t) + defer unregister() + + pluginNames, err := Scan() + if err != nil { + t.Fatal(err) + } + if pluginNames != nil { + t.Fatal("Plugin names should be empty.") + } + + path := filepath.Join(tmpdir, "echo.spec") + addr := "unix://var/lib/docker/plugins/echo.sock" + name := "echo" + + err = os.MkdirAll(filepath.Dir(path), 0755) + if err != nil { + t.Fatal(err) + } + + err = ioutil.WriteFile(path, []byte(addr), 0644) + if err != nil { + t.Fatal(err) + } + + r := newLocalRegistry() + p, err := r.Plugin(name) + + pluginNamesNotEmpty, err := Scan() + if err != nil { + t.Fatal(err) + } + if p.Name() != pluginNamesNotEmpty[0] { + t.Fatalf("Unable to scan plugin with name %s", p.name) + } +} diff --git a/pkg/plugins/plugin_test.go b/pkg/plugins/plugin_test.go index b19c0d52f1..00fcb85f58 100644 --- a/pkg/plugins/plugin_test.go +++ b/pkg/plugins/plugin_test.go @@ -1,12 +1,26 @@ package plugins import ( + "bytes" + "encoding/json" "errors" + "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/stretchr/testify/assert" +) + +const ( + fruitPlugin = "fruit" + fruitImplements = "apple" ) // regression test for deadlock in handlers @@ -42,3 +56,101 @@ func testActive(t *testing.T, p *Plugin) { } } + +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, err, ErrNotImplements) + + // check negative case where plugin vegetable doesn't exist + _, err = Get("vegetable", "potato") + assert.Equal(t, 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()) + } +} diff --git a/pkg/plugins/transport/http_test.go b/pkg/plugins/transport/http_test.go new file mode 100644 index 0000000000..b724fd0df0 --- /dev/null +++ b/pkg/plugins/transport/http_test.go @@ -0,0 +1,20 @@ +package transport + +import ( + "io" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHTTPTransport(t *testing.T) { + var r io.Reader + roundTripper := &http.Transport{} + newTransport := NewHTTPTransport(roundTripper, "http", "0.0.0.0") + request, err := newTransport.NewRequest("", r) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "POST", request.Method) +} diff --git a/plugin/manager_linux.go b/plugin/manager_linux.go index 5396b15bec..1e8994043f 100644 --- a/plugin/manager_linux.go +++ b/plugin/manager_linux.go @@ -80,7 +80,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error { func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error { sockAddr := filepath.Join(pm.config.ExecRoot, p.GetID(), p.GetSocket()) - client, err := plugins.NewClientWithTimeout("unix://"+sockAddr, nil, c.timeoutInSecs) + client, err := plugins.NewClientWithTimeout("unix://"+sockAddr, nil, time.Duration(c.timeoutInSecs)*time.Second) if err != nil { c.restart = false shutdownPlugin(p, c, pm.containerdClient)