github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/pkg/plugins/client_test.go (about)

     1  package plugins // import "github.com/docker/docker/pkg/plugins"
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"io"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"net/url"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/docker/docker/pkg/plugins/transport"
    16  	"github.com/docker/go-connections/tlsconfig"
    17  	"github.com/pkg/errors"
    18  	"gotest.tools/assert"
    19  	is "gotest.tools/assert/cmp"
    20  )
    21  
    22  var (
    23  	mux    *http.ServeMux
    24  	server *httptest.Server
    25  )
    26  
    27  func setupRemotePluginServer() string {
    28  	mux = http.NewServeMux()
    29  	server = httptest.NewServer(mux)
    30  	return server.URL
    31  }
    32  
    33  func teardownRemotePluginServer() {
    34  	if server != nil {
    35  		server.Close()
    36  	}
    37  }
    38  
    39  func TestFailedConnection(t *testing.T) {
    40  	c, _ := NewClient("tcp://127.0.0.1:1", &tlsconfig.Options{InsecureSkipVerify: true})
    41  	_, err := c.callWithRetry("Service.Method", nil, false)
    42  	if err == nil {
    43  		t.Fatal("Unexpected successful connection")
    44  	}
    45  }
    46  
    47  func TestFailOnce(t *testing.T) {
    48  	addr := setupRemotePluginServer()
    49  	defer teardownRemotePluginServer()
    50  
    51  	failed := false
    52  	mux.HandleFunc("/Test.FailOnce", func(w http.ResponseWriter, r *http.Request) {
    53  		if !failed {
    54  			failed = true
    55  			panic("Plugin not ready")
    56  		}
    57  	})
    58  
    59  	c, _ := NewClient(addr, &tlsconfig.Options{InsecureSkipVerify: true})
    60  	b := strings.NewReader("body")
    61  	_, err := c.callWithRetry("Test.FailOnce", b, true)
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  }
    66  
    67  func TestEchoInputOutput(t *testing.T) {
    68  	addr := setupRemotePluginServer()
    69  	defer teardownRemotePluginServer()
    70  
    71  	m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}}
    72  
    73  	mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) {
    74  		if r.Method != "POST" {
    75  			t.Fatalf("Expected POST, got %s\n", r.Method)
    76  		}
    77  
    78  		header := w.Header()
    79  		header.Set("Content-Type", transport.VersionMimetype)
    80  
    81  		io.Copy(w, r.Body)
    82  	})
    83  
    84  	c, _ := NewClient(addr, &tlsconfig.Options{InsecureSkipVerify: true})
    85  	var output Manifest
    86  	err := c.Call("Test.Echo", m, &output)
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  
    91  	assert.Check(t, is.DeepEqual(m, output))
    92  	err = c.Call("Test.Echo", nil, nil)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  }
    97  
    98  func TestBackoff(t *testing.T) {
    99  	cases := []struct {
   100  		retries    int
   101  		expTimeOff time.Duration
   102  	}{
   103  		{0, time.Duration(1)},
   104  		{1, time.Duration(2)},
   105  		{2, time.Duration(4)},
   106  		{4, time.Duration(16)},
   107  		{6, time.Duration(30)},
   108  		{10, time.Duration(30)},
   109  	}
   110  
   111  	for _, c := range cases {
   112  		s := c.expTimeOff * time.Second
   113  		if d := backoff(c.retries); d != s {
   114  			t.Fatalf("Retry %v, expected %v, was %v\n", c.retries, s, d)
   115  		}
   116  	}
   117  }
   118  
   119  func TestAbortRetry(t *testing.T) {
   120  	cases := []struct {
   121  		timeOff  time.Duration
   122  		expAbort bool
   123  	}{
   124  		{time.Duration(1), false},
   125  		{time.Duration(2), false},
   126  		{time.Duration(10), false},
   127  		{time.Duration(30), true},
   128  		{time.Duration(40), true},
   129  	}
   130  
   131  	for _, c := range cases {
   132  		s := c.timeOff * time.Second
   133  		if a := abort(time.Now(), s); a != c.expAbort {
   134  			t.Fatalf("Duration %v, expected %v, was %v\n", c.timeOff, s, a)
   135  		}
   136  	}
   137  }
   138  
   139  func TestClientScheme(t *testing.T) {
   140  	cases := map[string]string{
   141  		"tcp://127.0.0.1:8080":          "http",
   142  		"unix:///usr/local/plugins/foo": "http",
   143  		"http://127.0.0.1:8080":         "http",
   144  		"https://127.0.0.1:8080":        "https",
   145  	}
   146  
   147  	for addr, scheme := range cases {
   148  		u, err := url.Parse(addr)
   149  		if err != nil {
   150  			t.Fatal(err)
   151  		}
   152  		s := httpScheme(u)
   153  
   154  		if s != scheme {
   155  			t.Fatalf("URL scheme mismatch, expected %s, got %s", scheme, s)
   156  		}
   157  	}
   158  }
   159  
   160  func TestNewClientWithTimeout(t *testing.T) {
   161  	addr := setupRemotePluginServer()
   162  	defer teardownRemotePluginServer()
   163  
   164  	m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}}
   165  
   166  	mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) {
   167  		time.Sleep(time.Duration(600) * time.Millisecond)
   168  		io.Copy(w, r.Body)
   169  	})
   170  
   171  	// setting timeout of 500ms
   172  	timeout := time.Duration(500) * time.Millisecond
   173  	c, _ := NewClientWithTimeout(addr, &tlsconfig.Options{InsecureSkipVerify: true}, timeout)
   174  	var output Manifest
   175  	err := c.Call("Test.Echo", m, &output)
   176  	if err == nil {
   177  		t.Fatal("Expected timeout error")
   178  	}
   179  }
   180  
   181  func TestClientStream(t *testing.T) {
   182  	addr := setupRemotePluginServer()
   183  	defer teardownRemotePluginServer()
   184  
   185  	m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}}
   186  	var output Manifest
   187  
   188  	mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) {
   189  		if r.Method != "POST" {
   190  			t.Fatalf("Expected POST, got %s", r.Method)
   191  		}
   192  
   193  		header := w.Header()
   194  		header.Set("Content-Type", transport.VersionMimetype)
   195  
   196  		io.Copy(w, r.Body)
   197  	})
   198  
   199  	c, _ := NewClient(addr, &tlsconfig.Options{InsecureSkipVerify: true})
   200  	body, err := c.Stream("Test.Echo", m)
   201  	if err != nil {
   202  		t.Fatal(err)
   203  	}
   204  	defer body.Close()
   205  	if err := json.NewDecoder(body).Decode(&output); err != nil {
   206  		t.Fatalf("Test.Echo: error reading plugin resp: %v", err)
   207  	}
   208  	assert.Check(t, is.DeepEqual(m, output))
   209  }
   210  
   211  func TestClientSendFile(t *testing.T) {
   212  	addr := setupRemotePluginServer()
   213  	defer teardownRemotePluginServer()
   214  
   215  	m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}}
   216  	var output Manifest
   217  	var buf bytes.Buffer
   218  	if err := json.NewEncoder(&buf).Encode(m); err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) {
   222  		if r.Method != "POST" {
   223  			t.Fatalf("Expected POST, got %s\n", r.Method)
   224  		}
   225  
   226  		header := w.Header()
   227  		header.Set("Content-Type", transport.VersionMimetype)
   228  
   229  		io.Copy(w, r.Body)
   230  	})
   231  
   232  	c, _ := NewClient(addr, &tlsconfig.Options{InsecureSkipVerify: true})
   233  	if err := c.SendFile("Test.Echo", &buf, &output); err != nil {
   234  		t.Fatal(err)
   235  	}
   236  	assert.Check(t, is.DeepEqual(m, output))
   237  }
   238  
   239  func TestClientWithRequestTimeout(t *testing.T) {
   240  	timeout := 1 * time.Millisecond
   241  	testHandler := func(w http.ResponseWriter, r *http.Request) {
   242  		time.Sleep(timeout + 1*time.Millisecond)
   243  		w.WriteHeader(http.StatusOK)
   244  	}
   245  
   246  	srv := httptest.NewServer(http.HandlerFunc(testHandler))
   247  	defer srv.Close()
   248  
   249  	client := &Client{http: srv.Client(), requestFactory: &testRequestWrapper{srv}}
   250  	_, err := client.callWithRetry("/Plugin.Hello", nil, false, WithRequestTimeout(timeout))
   251  	assert.Assert(t, is.ErrorContains(err, ""), "expected error")
   252  
   253  	err = errors.Cause(err)
   254  
   255  	switch e := err.(type) {
   256  	case *url.Error:
   257  		err = e.Err
   258  	}
   259  	assert.DeepEqual(t, context.DeadlineExceeded, err)
   260  }
   261  
   262  type testRequestWrapper struct {
   263  	*httptest.Server
   264  }
   265  
   266  func (w *testRequestWrapper) NewRequest(path string, data io.Reader) (*http.Request, error) {
   267  	req, err := http.NewRequest("POST", path, data)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  	u, err := url.Parse(w.Server.URL)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	req.URL = u
   276  	return req, nil
   277  }