client_test.go 6.5 KB

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