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 }