1// Copyright 2013 go-dockerclient authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package docker
6
7import (
8	"bufio"
9	"bytes"
10	"context"
11	"encoding/json"
12	"errors"
13	"fmt"
14	"io/ioutil"
15	"net"
16	"net/http"
17	"net/http/httptest"
18	"net/url"
19	"os"
20	"reflect"
21	"strconv"
22	"strings"
23	"testing"
24	"time"
25)
26
27func TestStateString(t *testing.T) {
28	t.Parallel()
29	started := time.Now().Add(-3 * time.Hour)
30	var tests = []struct {
31		input    State
32		expected string
33	}{
34		{State{Running: true, Paused: true, StartedAt: started}, "Up 3 hours (Paused)"},
35		{State{Running: true, Restarting: true, ExitCode: 7, FinishedAt: started}, "Restarting (7) 3 hours ago"},
36		{State{Running: true, StartedAt: started}, "Up 3 hours"},
37		{State{RemovalInProgress: true}, "Removal In Progress"},
38		{State{Dead: true}, "Dead"},
39		{State{}, "Created"},
40		{State{StartedAt: started}, ""},
41		{State{ExitCode: 7, StartedAt: started, FinishedAt: started}, "Exited (7) 3 hours ago"},
42	}
43	for _, tt := range tests {
44		if got := tt.input.String(); got != tt.expected {
45			t.Errorf("State.String(): wrong result. Want %q. Got %q.", tt.expected, got)
46		}
47	}
48}
49
50func TestStateStateString(t *testing.T) {
51	t.Parallel()
52	started := time.Now().Add(-3 * time.Hour)
53	var tests = []struct {
54		input    State
55		expected string
56	}{
57		{State{Running: true, Paused: true}, "paused"},
58		{State{Running: true, Restarting: true}, "restarting"},
59		{State{Running: true}, "running"},
60		{State{Dead: true}, "dead"},
61		{State{}, "created"},
62		{State{StartedAt: started}, "exited"},
63	}
64	for _, tt := range tests {
65		if got := tt.input.StateString(); got != tt.expected {
66			t.Errorf("State.String(): wrong result. Want %q. Got %q.", tt.expected, got)
67		}
68	}
69}
70
71func TestListContainers(t *testing.T) {
72	t.Parallel()
73	jsonContainers := `[
74     {
75             "Id": "8dfafdbc3a40",
76             "Image": "base:latest",
77             "Command": "echo 1",
78             "Created": 1367854155,
79             "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
80             "Status": "Exit 0"
81     },
82     {
83             "Id": "9cd87474be90",
84             "Image": "base:latest",
85             "Command": "echo 222222",
86             "Created": 1367854155,
87             "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
88             "Status": "Exit 0"
89     },
90     {
91             "Id": "3176a2479c92",
92             "Image": "base:latest",
93             "Command": "echo 3333333333333333",
94             "Created": 1367854154,
95             "Ports":[{"PrivatePort": 2221, "PublicPort": 3331, "Type": "tcp"}],
96             "Status": "Exit 0"
97     },
98     {
99             "Id": "4cb07b47f9fb",
100             "Image": "base:latest",
101             "Command": "echo 444444444444444444444444444444444",
102             "Ports":[{"PrivatePort": 2223, "PublicPort": 3332, "Type": "tcp"}],
103             "Created": 1367854152,
104             "Status": "Exit 0"
105     }
106]`
107	var expected []APIContainers
108	err := json.Unmarshal([]byte(jsonContainers), &expected)
109	if err != nil {
110		t.Fatal(err)
111	}
112	client := newTestClient(&FakeRoundTripper{message: jsonContainers, status: http.StatusOK})
113	containers, err := client.ListContainers(ListContainersOptions{})
114	if err != nil {
115		t.Fatal(err)
116	}
117	if !reflect.DeepEqual(containers, expected) {
118		t.Errorf("ListContainers: Expected %#v. Got %#v.", expected, containers)
119	}
120}
121
122func TestListContainersParams(t *testing.T) {
123	t.Parallel()
124	var tests = []struct {
125		input  ListContainersOptions
126		params map[string][]string
127	}{
128		{ListContainersOptions{}, map[string][]string{}},
129		{ListContainersOptions{All: true}, map[string][]string{"all": {"1"}}},
130		{ListContainersOptions{All: true, Limit: 10}, map[string][]string{"all": {"1"}, "limit": {"10"}}},
131		{
132			ListContainersOptions{All: true, Limit: 10, Since: "adf9983", Before: "abdeef"},
133			map[string][]string{"all": {"1"}, "limit": {"10"}, "since": {"adf9983"}, "before": {"abdeef"}},
134		},
135		{
136			ListContainersOptions{Filters: map[string][]string{"status": {"paused", "running"}}},
137			map[string][]string{"filters": {"{\"status\":[\"paused\",\"running\"]}"}},
138		},
139		{
140			ListContainersOptions{All: true, Filters: map[string][]string{"exited": {"0"}, "status": {"exited"}}},
141			map[string][]string{"all": {"1"}, "filters": {"{\"exited\":[\"0\"],\"status\":[\"exited\"]}"}},
142		},
143	}
144	fakeRT := &FakeRoundTripper{message: "[]", status: http.StatusOK}
145	client := newTestClient(fakeRT)
146	u, _ := url.Parse(client.getURL("/containers/json"))
147	for _, tt := range tests {
148		if _, err := client.ListContainers(tt.input); err != nil {
149			t.Error(err)
150		}
151		got := map[string][]string(fakeRT.requests[0].URL.Query())
152		if !reflect.DeepEqual(got, tt.params) {
153			t.Errorf("Expected %#v, got %#v.", tt.params, got)
154		}
155		if path := fakeRT.requests[0].URL.Path; path != u.Path {
156			t.Errorf("Wrong path on request. Want %q. Got %q.", u.Path, path)
157		}
158		if meth := fakeRT.requests[0].Method; meth != "GET" {
159			t.Errorf("Wrong HTTP method. Want GET. Got %s.", meth)
160		}
161		fakeRT.Reset()
162	}
163}
164
165func TestListContainersFailure(t *testing.T) {
166	t.Parallel()
167	var tests = []struct {
168		status  int
169		message string
170	}{
171		{400, "bad parameter"},
172		{500, "internal server error"},
173	}
174	for _, tt := range tests {
175		client := newTestClient(&FakeRoundTripper{message: tt.message, status: tt.status})
176		expected := Error{Status: tt.status, Message: tt.message}
177		containers, err := client.ListContainers(ListContainersOptions{})
178		if !reflect.DeepEqual(expected, *err.(*Error)) {
179			t.Errorf("Wrong error in ListContainers. Want %#v. Got %#v.", expected, err)
180		}
181		if len(containers) > 0 {
182			t.Errorf("ListContainers failure. Expected empty list. Got %#v.", containers)
183		}
184	}
185}
186
187func TestInspectContainer(t *testing.T) {
188	t.Parallel()
189	jsonContainer := `{
190             "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
191             "AppArmorProfile": "Profile",
192             "Created": "2013-05-07T14:51:42.087658+02:00",
193             "Path": "date",
194             "Args": [],
195             "Config": {
196                     "Hostname": "4fa6e0f0c678",
197                     "User": "",
198                     "Memory": 17179869184,
199                     "MemorySwap": 34359738368,
200                     "AttachStdin": false,
201                     "AttachStdout": true,
202                     "AttachStderr": true,
203                     "PortSpecs": null,
204                     "Tty": false,
205                     "OpenStdin": false,
206                     "StdinOnce": false,
207                     "Env": null,
208                     "Cmd": [
209                             "date"
210                     ],
211                     "Image": "base",
212                     "Volumes": {},
213                     "VolumesFrom": "",
214                     "SecurityOpt": [
215                         "label:user:USER"
216                      ],
217                      "Ulimits": [
218                          { "Name": "nofile", "Soft": 1024, "Hard": 2048 }
219											],
220											"Shell": [
221                         "/bin/sh", "-c"
222											]
223             },
224             "State": {
225                     "Running": false,
226                     "Pid": 0,
227                     "ExitCode": 0,
228                     "StartedAt": "2013-05-07T14:51:42.087658+02:00",
229                     "Ghost": false
230             },
231             "Node": {
232                  "ID": "4I4E:QR4I:Z733:QEZK:5X44:Q4T7:W2DD:JRDY:KB2O:PODO:Z5SR:XRB6",
233                  "IP": "192.168.99.105",
234                  "Addra": "192.168.99.105:2376",
235                  "Name": "node-01",
236                  "Cpus": 4,
237                  "Memory": 1048436736,
238                  "Labels": {
239                      "executiondriver": "native-0.2",
240                      "kernelversion": "3.18.5-tinycore64",
241                      "operatingsystem": "Boot2Docker 1.5.0 (TCL 5.4); master : a66bce5 - Tue Feb 10 23:31:27 UTC 2015",
242                      "provider": "virtualbox",
243                      "storagedriver": "aufs"
244                  }
245              },
246             "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
247             "NetworkSettings": {
248                     "IpAddress": "",
249                     "IpPrefixLen": 0,
250                     "Gateway": "",
251                     "Bridge": "",
252                     "PortMapping": null
253             },
254             "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
255             "ResolvConfPath": "/etc/resolv.conf",
256             "Volumes": {},
257             "HostConfig": {
258               "Binds": null,
259               "ContainerIDFile": "",
260               "LxcConf": [],
261               "Privileged": false,
262               "PortBindings": {
263                 "80/tcp": [
264                   {
265                     "HostIp": "0.0.0.0",
266                     "HostPort": "49153"
267                   }
268                 ]
269               },
270               "Links": null,
271               "PublishAllPorts": false,
272               "CgroupParent": "/mesos",
273               "Memory": 17179869184,
274               "MemorySwap": 34359738368,
275               "GroupAdd": ["fake", "12345"],
276               "OomScoreAdj": 642
277             }
278}`
279	var expected Container
280	err := json.Unmarshal([]byte(jsonContainer), &expected)
281	if err != nil {
282		t.Fatal(err)
283	}
284	fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
285	client := newTestClient(fakeRT)
286	id := "4fa6e0f0c678"
287	container, err := client.InspectContainer(id)
288	if err != nil {
289		t.Fatal(err)
290	}
291	if !reflect.DeepEqual(*container, expected) {
292		t.Errorf("InspectContainer(%q): Expected %#v. Got %#v.", id, expected, container)
293	}
294	expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/json"))
295	if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
296		t.Errorf("InspectContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
297	}
298}
299
300func TestInspectContainerWithContext(t *testing.T) {
301	t.Parallel()
302	jsonContainer := `{
303             "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
304             "AppArmorProfile": "Profile",
305             "Created": "2013-05-07T14:51:42.087658+02:00",
306             "Path": "date",
307             "Args": [],
308             "Config": {
309                     "Hostname": "4fa6e0f0c678",
310                     "User": "",
311                     "Memory": 17179869184,
312                     "MemorySwap": 34359738368,
313                     "AttachStdin": false,
314                     "AttachStdout": true,
315                     "AttachStderr": true,
316                     "PortSpecs": null,
317                     "Tty": false,
318                     "OpenStdin": false,
319                     "StdinOnce": false,
320                     "Env": null,
321                     "Cmd": [
322                             "date"
323                     ],
324                     "Image": "base",
325                     "Volumes": {},
326                     "VolumesFrom": "",
327                     "SecurityOpt": [
328                         "label:user:USER"
329                      ],
330                      "Ulimits": [
331                          { "Name": "nofile", "Soft": 1024, "Hard": 2048 }
332                      ]
333             },
334             "State": {
335                     "Running": false,
336                     "Pid": 0,
337                     "ExitCode": 0,
338                     "StartedAt": "2013-05-07T14:51:42.087658+02:00",
339                     "Ghost": false
340             },
341             "Node": {
342                  "ID": "4I4E:QR4I:Z733:QEZK:5X44:Q4T7:W2DD:JRDY:KB2O:PODO:Z5SR:XRB6",
343                  "IP": "192.168.99.105",
344                  "Addra": "192.168.99.105:2376",
345                  "Name": "node-01",
346                  "Cpus": 4,
347                  "Memory": 1048436736,
348                  "Labels": {
349                      "executiondriver": "native-0.2",
350                      "kernelversion": "3.18.5-tinycore64",
351                      "operatingsystem": "Boot2Docker 1.5.0 (TCL 5.4); master : a66bce5 - Tue Feb 10 23:31:27 UTC 2015",
352                      "provider": "virtualbox",
353                      "storagedriver": "aufs"
354                  }
355              },
356             "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
357             "NetworkSettings": {
358                     "IpAddress": "",
359                     "IpPrefixLen": 0,
360                     "Gateway": "",
361                     "Bridge": "",
362                     "PortMapping": null
363             },
364             "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
365             "ResolvConfPath": "/etc/resolv.conf",
366             "Volumes": {},
367             "HostConfig": {
368               "Binds": null,
369               "BlkioDeviceReadIOps": [
370                   {
371                       "Path": "/dev/sdb",
372                       "Rate": 100
373                   }
374               ],
375               "BlkioDeviceWriteBps": [
376                   {
377                       "Path": "/dev/sdb",
378                       "Rate": 5000
379                   }
380               ],
381               "ContainerIDFile": "",
382               "LxcConf": [],
383               "Privileged": false,
384               "PortBindings": {
385                 "80/tcp": [
386                   {
387                     "HostIp": "0.0.0.0",
388                     "HostPort": "49153"
389                   }
390                 ]
391               },
392               "Links": null,
393               "PublishAllPorts": false,
394               "CgroupParent": "/mesos",
395               "Memory": 17179869184,
396               "MemorySwap": 34359738368,
397               "GroupAdd": ["fake", "12345"],
398               "OomScoreAdj": 642
399             }
400}`
401	var expected Container
402	err := json.Unmarshal([]byte(jsonContainer), &expected)
403	if err != nil {
404		t.Fatal(err)
405	}
406	fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
407	client := newTestClient(fakeRT)
408	id := "4fa6e0f0c678"
409
410	ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
411	defer cancel()
412
413	inspectError := make(chan error)
414	// Invoke InspectContainer in a goroutine. The response is sent to the 'inspectError'
415	// channel.
416	go func() {
417		container, err := client.InspectContainer(id)
418		if err != nil {
419			inspectError <- err
420			return
421		}
422		if !reflect.DeepEqual(*container, expected) {
423			inspectError <- fmt.Errorf("inspectContainer(%q): Expected %#v. Got %#v", id, expected, container)
424			return
425		}
426		expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/json"))
427		if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
428			inspectError <- fmt.Errorf("inspectContainer(%q): Wrong path in request. Want %q. Got %q", id, expectedURL.Path, gotPath)
429			return
430		}
431		// No errors to tbe reported. Send 'nil'
432		inspectError <- nil
433	}()
434	// Wait for either the inspect response or for the context.
435	select {
436	case err := <-inspectError:
437		if err != nil {
438			t.Fatalf("Error inspecting container with context: %v", err)
439		}
440	case <-ctx.Done():
441		// Context was canceled unexpectedly. Report the same.
442		t.Fatalf("Context canceled when waiting for inspect container response: %v", ctx.Err())
443	}
444}
445
446func TestInspectContainerNetwork(t *testing.T) {
447	t.Parallel()
448	jsonContainer := `{
449            "Id": "81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c",
450            "Created": "2015-11-12T14:54:04.791485659Z",
451            "Path": "consul-template",
452            "Args": [
453                "-config=/tmp/haproxy.json",
454                "-consul=192.168.99.120:8500"
455            ],
456            "State": {
457                "Status": "running",
458                "Running": true,
459                "Paused": false,
460                "Restarting": false,
461                "OOMKilled": false,
462                "Dead": false,
463                "Pid": 3196,
464                "ExitCode": 0,
465                "Error": "",
466                "StartedAt": "2015-11-12T14:54:05.026747471Z",
467                "FinishedAt": "0001-01-01T00:00:00Z"
468            },
469            "Image": "4921c5917fc117df3dec32f4c1976635dc6c56ccd3336fe1db3477f950e78bf7",
470            "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/resolv.conf",
471            "HostnamePath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/hostname",
472            "HostsPath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/hosts",
473            "LogPath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c-json.log",
474            "Node": {
475                "ID": "AUIB:LFOT:3LSF:SCFS:OYDQ:NLXD:JZNE:4INI:3DRC:ZFBB:GWCY:DWJK",
476                "IP": "192.168.99.121",
477                "Addr": "192.168.99.121:2376",
478                "Name": "swl-demo1",
479                "Cpus": 1,
480                "Memory": 2099945472,
481                "Labels": {
482                    "executiondriver": "native-0.2",
483                    "kernelversion": "4.1.12-boot2docker",
484                    "operatingsystem": "Boot2Docker 1.9.0 (TCL 6.4); master : 16e4a2a - Tue Nov  3 19:49:22 UTC 2015",
485                    "provider": "virtualbox",
486                    "storagedriver": "aufs"
487                }
488            },
489            "Name": "/docker-proxy.swl-demo1",
490            "RestartCount": 0,
491            "Driver": "aufs",
492            "ExecDriver": "native-0.2",
493            "MountLabel": "",
494            "ProcessLabel": "",
495            "AppArmorProfile": "",
496            "ExecIDs": null,
497            "HostConfig": {
498                "Binds": null,
499                "ContainerIDFile": "",
500                "LxcConf": [],
501                "Memory": 0,
502                "MemoryReservation": 0,
503                "MemorySwap": 0,
504                "KernelMemory": 0,
505                "CpuShares": 0,
506                "CpuPeriod": 0,
507                "CpusetCpus": "",
508                "CpusetMems": "",
509                "CpuQuota": 0,
510                "BlkioWeight": 0,
511                "OomKillDisable": false,
512                "MemorySwappiness": -1,
513                "Privileged": false,
514                "PortBindings": {
515                    "443/tcp": [
516                        {
517                            "HostIp": "",
518                            "HostPort": "443"
519                        }
520                    ]
521                },
522                "Links": null,
523                "PublishAllPorts": false,
524                "Dns": null,
525                "DnsOptions": null,
526                "DnsSearch": null,
527                "ExtraHosts": null,
528                "VolumesFrom": null,
529                "Devices": [],
530                "NetworkMode": "swl-net",
531                "IpcMode": "",
532                "PidMode": "",
533                "UTSMode": "",
534                "CapAdd": null,
535                "CapDrop": null,
536                "GroupAdd": null,
537                "RestartPolicy": {
538                    "Name": "no",
539                    "MaximumRetryCount": 0
540                },
541                "SecurityOpt": null,
542                "ReadonlyRootfs": false,
543                "Ulimits": null,
544                "LogConfig": {
545                    "Type": "json-file",
546                    "Config": {}
547                },
548                "CgroupParent": "",
549                "ConsoleSize": [
550                    0,
551                    0
552                ],
553                "VolumeDriver": ""
554            },
555            "GraphDriver": {
556                "Name": "aufs",
557                "Data": null
558            },
559            "Mounts": [],
560            "Config": {
561                "Hostname": "81e1bbe20b55",
562                "Domainname": "",
563                "User": "",
564                "AttachStdin": false,
565                "AttachStdout": false,
566                "AttachStderr": false,
567                "ExposedPorts": {
568                    "443/tcp": {}
569                },
570                "Tty": false,
571                "OpenStdin": false,
572                "StdinOnce": false,
573                "Env": [
574                    "DOMAIN=local.auto",
575                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
576                    "CONSUL_TEMPLATE_VERSION=0.11.1"
577                ],
578                "Cmd": [
579                    "-consul=192.168.99.120:8500"
580                ],
581                "Image": "docker-proxy:latest",
582                "Volumes": null,
583                "WorkingDir": "",
584                "Entrypoint": [
585                    "consul-template",
586                    "-config=/tmp/haproxy.json"
587                ],
588                "OnBuild": null,
589                "Labels": {},
590                "StopSignal": "SIGTERM"
591            },
592            "NetworkSettings": {
593                "Bridge": "",
594                "SandboxID": "c6b903dc5c1a96113a22dbc44709e30194079bd2d262eea1eb4f38d85821f6e1",
595                "HairpinMode": false,
596                "LinkLocalIPv6Address": "",
597                "LinkLocalIPv6PrefixLen": 0,
598                "Ports": {
599                    "443/tcp": [
600                        {
601                            "HostIp": "192.168.99.121",
602                            "HostPort": "443"
603                        }
604                    ]
605                },
606                "SandboxKey": "/var/run/docker/netns/c6b903dc5c1a",
607                "SecondaryIPAddresses": null,
608                "SecondaryIPv6Addresses": null,
609                "EndpointID": "",
610                "Gateway": "",
611                "GlobalIPv6Address": "",
612                "GlobalIPv6PrefixLen": 0,
613                "IPAddress": "",
614                "IPPrefixLen": 0,
615                "IPv6Gateway": "",
616                "MacAddress": "",
617                "Networks": {
618                    "swl-net": {
619						"Aliases": [
620							"testalias",
621							"81e1bbe20b55"
622						],
623                        "NetworkID": "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
624                        "EndpointID": "683e3092275782a53c3b0968cc7e3a10f23264022ded9cb20490902f96fc5981",
625                        "Gateway": "",
626                        "IPAddress": "10.0.0.3",
627                        "IPPrefixLen": 24,
628                        "IPv6Gateway": "",
629                        "GlobalIPv6Address": "",
630                        "GlobalIPv6PrefixLen": 0,
631                        "MacAddress": "02:42:0a:00:00:03"
632                    }
633                }
634            }
635}`
636
637	fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
638	client := newTestClient(fakeRT)
639	id := "81e1bbe20b55"
640	expIP := "10.0.0.3"
641	expNetworkID := "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812"
642	expectedAliases := []string{"testalias", "81e1bbe20b55"}
643
644	container, err := client.InspectContainer(id)
645	if err != nil {
646		t.Fatal(err)
647	}
648
649	s := reflect.Indirect(reflect.ValueOf(container.NetworkSettings))
650	networks := s.FieldByName("Networks")
651	if networks.IsValid() {
652		var ip string
653		for _, net := range networks.MapKeys() {
654			if net.Interface().(string) == container.HostConfig.NetworkMode {
655				ip = networks.MapIndex(net).FieldByName("IPAddress").Interface().(string)
656				t.Logf("%s %v", net, ip)
657			}
658		}
659		if ip != expIP {
660			t.Errorf("InspectContainerNetworks(%q): Expected %#v. Got %#v.", id, expIP, ip)
661		}
662
663		var networkID string
664		for _, net := range networks.MapKeys() {
665			if net.Interface().(string) == container.HostConfig.NetworkMode {
666				networkID = networks.MapIndex(net).FieldByName("NetworkID").Interface().(string)
667				t.Logf("%s %v", net, networkID)
668			}
669		}
670
671		var aliases []string
672		for _, net := range networks.MapKeys() {
673			if net.Interface().(string) == container.HostConfig.NetworkMode {
674				aliases = networks.MapIndex(net).FieldByName("Aliases").Interface().([]string)
675			}
676		}
677		if !reflect.DeepEqual(aliases, expectedAliases) {
678			t.Errorf("InspectContainerNetworks(%q): Expected Aliases %#v. Got %#v.", id, expectedAliases, aliases)
679		}
680
681		if networkID != expNetworkID {
682			t.Errorf("InspectContainerNetworks(%q): Expected %#v. Got %#v.", id, expNetworkID, networkID)
683		}
684	} else {
685		t.Errorf("InspectContainerNetworks(%q): No method Networks for NetworkSettings", id)
686	}
687
688}
689
690func TestInspectContainerNegativeSwap(t *testing.T) {
691	t.Parallel()
692	jsonContainer := `{
693             "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
694             "Created": "2013-05-07T14:51:42.087658+02:00",
695             "Path": "date",
696             "Args": [],
697             "Config": {
698                     "Hostname": "4fa6e0f0c678",
699                     "User": "",
700                     "Memory": 17179869184,
701                     "MemorySwap": -1,
702                     "AttachStdin": false,
703                     "AttachStdout": true,
704                     "AttachStderr": true,
705                     "PortSpecs": null,
706                     "Tty": false,
707                     "OpenStdin": false,
708                     "StdinOnce": false,
709                     "Env": null,
710                     "Cmd": [
711                             "date"
712                     ],
713                     "Image": "base",
714                     "Volumes": {},
715                     "VolumesFrom": ""
716             },
717             "State": {
718                     "Running": false,
719                     "Pid": 0,
720                     "ExitCode": 0,
721                     "StartedAt": "2013-05-07T14:51:42.087658+02:00",
722                     "Ghost": false
723             },
724             "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
725             "NetworkSettings": {
726                     "IpAddress": "",
727                     "IpPrefixLen": 0,
728                     "Gateway": "",
729                     "Bridge": "",
730                     "PortMapping": null
731             },
732             "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker",
733             "ResolvConfPath": "/etc/resolv.conf",
734             "Volumes": {},
735             "HostConfig": {
736               "Binds": null,
737               "ContainerIDFile": "",
738               "LxcConf": [],
739               "Privileged": false,
740               "PortBindings": {
741                 "80/tcp": [
742                   {
743                     "HostIp": "0.0.0.0",
744                     "HostPort": "49153"
745                   }
746                 ]
747               },
748               "Links": null,
749               "PublishAllPorts": false
750             }
751}`
752	var expected Container
753	err := json.Unmarshal([]byte(jsonContainer), &expected)
754	if err != nil {
755		t.Fatal(err)
756	}
757	fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
758	client := newTestClient(fakeRT)
759	id := "4fa6e0f0c678"
760	container, err := client.InspectContainer(id)
761	if err != nil {
762		t.Fatal(err)
763	}
764	if !reflect.DeepEqual(*container, expected) {
765		t.Errorf("InspectContainer(%q): Expected %#v. Got %#v.", id, expected, container)
766	}
767	expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/json"))
768	if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
769		t.Errorf("InspectContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
770	}
771}
772
773func TestInspectContainerFailure(t *testing.T) {
774	t.Parallel()
775	client := newTestClient(&FakeRoundTripper{message: "server error", status: 500})
776	expected := Error{Status: 500, Message: "server error"}
777	container, err := client.InspectContainer("abe033")
778	if container != nil {
779		t.Errorf("InspectContainer: Expected <nil> container, got %#v", container)
780	}
781	if !reflect.DeepEqual(expected, *err.(*Error)) {
782		t.Errorf("InspectContainer: Wrong error information. Want %#v. Got %#v.", expected, err)
783	}
784}
785
786func TestInspectContainerNotFound(t *testing.T) {
787	t.Parallel()
788	client := newTestClient(&FakeRoundTripper{message: "no such container", status: 404})
789	container, err := client.InspectContainer("abe033")
790	if container != nil {
791		t.Errorf("InspectContainer: Expected <nil> container, got %#v", container)
792	}
793	expected := &NoSuchContainer{ID: "abe033"}
794	if !reflect.DeepEqual(err, expected) {
795		t.Errorf("InspectContainer: Wrong error information. Want %#v. Got %#v.", expected, err)
796	}
797}
798
799func TestContainerChanges(t *testing.T) {
800	t.Parallel()
801	jsonChanges := `[
802     {
803             "Path":"/dev",
804             "Kind":0
805     },
806     {
807             "Path":"/dev/kmsg",
808             "Kind":1
809     },
810     {
811             "Path":"/test",
812             "Kind":1
813     }
814]`
815	var expected []Change
816	err := json.Unmarshal([]byte(jsonChanges), &expected)
817	if err != nil {
818		t.Fatal(err)
819	}
820	fakeRT := &FakeRoundTripper{message: jsonChanges, status: http.StatusOK}
821	client := newTestClient(fakeRT)
822	id := "4fa6e0f0c678"
823	changes, err := client.ContainerChanges(id)
824	if err != nil {
825		t.Fatal(err)
826	}
827	if !reflect.DeepEqual(changes, expected) {
828		t.Errorf("ContainerChanges(%q): Expected %#v. Got %#v.", id, expected, changes)
829	}
830	expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/changes"))
831	if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
832		t.Errorf("ContainerChanges(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
833	}
834}
835
836func TestContainerChangesFailure(t *testing.T) {
837	t.Parallel()
838	client := newTestClient(&FakeRoundTripper{message: "server error", status: 500})
839	expected := Error{Status: 500, Message: "server error"}
840	changes, err := client.ContainerChanges("abe033")
841	if changes != nil {
842		t.Errorf("ContainerChanges: Expected <nil> changes, got %#v", changes)
843	}
844	if !reflect.DeepEqual(expected, *err.(*Error)) {
845		t.Errorf("ContainerChanges: Wrong error information. Want %#v. Got %#v.", expected, err)
846	}
847}
848
849func TestContainerChangesNotFound(t *testing.T) {
850	t.Parallel()
851	client := newTestClient(&FakeRoundTripper{message: "no such container", status: 404})
852	changes, err := client.ContainerChanges("abe033")
853	if changes != nil {
854		t.Errorf("ContainerChanges: Expected <nil> changes, got %#v", changes)
855	}
856	expected := &NoSuchContainer{ID: "abe033"}
857	if !reflect.DeepEqual(err, expected) {
858		t.Errorf("ContainerChanges: Wrong error information. Want %#v. Got %#v.", expected, err)
859	}
860}
861
862func TestCreateContainer(t *testing.T) {
863	t.Parallel()
864	jsonContainer := `{
865             "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
866	     "Warnings": []
867}`
868	var expected Container
869	err := json.Unmarshal([]byte(jsonContainer), &expected)
870	if err != nil {
871		t.Fatal(err)
872	}
873	fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
874	client := newTestClient(fakeRT)
875	config := Config{AttachStdout: true, AttachStdin: true}
876	opts := CreateContainerOptions{Name: "TestCreateContainer", Config: &config}
877	container, err := client.CreateContainer(opts)
878	if err != nil {
879		t.Fatal(err)
880	}
881	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
882	if container.ID != id {
883		t.Errorf("CreateContainer: wrong ID. Want %q. Got %q.", id, container.ID)
884	}
885	req := fakeRT.requests[0]
886	if req.Method != "POST" {
887		t.Errorf("CreateContainer: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
888	}
889	expectedURL, _ := url.Parse(client.getURL("/containers/create"))
890	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
891		t.Errorf("CreateContainer: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
892	}
893	var gotBody Config
894	err = json.NewDecoder(req.Body).Decode(&gotBody)
895	if err != nil {
896		t.Fatal(err)
897	}
898}
899
900func TestCreateContainerImageNotFound(t *testing.T) {
901	t.Parallel()
902	client := newTestClient(&FakeRoundTripper{message: "No such image", status: http.StatusNotFound})
903	config := Config{AttachStdout: true, AttachStdin: true}
904	container, err := client.CreateContainer(CreateContainerOptions{Config: &config})
905	if container != nil {
906		t.Errorf("CreateContainer: expected <nil> container, got %#v.", container)
907	}
908	if !reflect.DeepEqual(err, ErrNoSuchImage) {
909		t.Errorf("CreateContainer: Wrong error type. Want %#v. Got %#v.", ErrNoSuchImage, err)
910	}
911}
912
913func TestCreateContainerDuplicateName(t *testing.T) {
914	t.Parallel()
915	client := newTestClient(&FakeRoundTripper{message: "No such image", status: http.StatusConflict})
916	config := Config{AttachStdout: true, AttachStdin: true}
917	container, err := client.CreateContainer(CreateContainerOptions{Config: &config})
918	if container != nil {
919		t.Errorf("CreateContainer: expected <nil> container, got %#v.", container)
920	}
921	if err != ErrContainerAlreadyExists {
922		t.Errorf("CreateContainer: Wrong error type. Want %#v. Got %#v.", ErrContainerAlreadyExists, err)
923	}
924}
925
926// Workaround for 17.09 bug returning 400 instead of 409.
927// See https://github.com/moby/moby/issues/35021
928func TestCreateContainerDuplicateNameWorkaroundDocker17_09(t *testing.T) {
929	t.Parallel()
930	client := newTestClient(&FakeRoundTripper{message: `{"message":"Conflict. The container name \"/c1\" is already in use by container \"2ce137e165dfca5e087f247b5d05a2311f91ef3da4bb7772168446a1a47e2f68\". You have to remove (or rename) that container to be able to reuse that name."}`, status: http.StatusBadRequest})
931	config := Config{AttachStdout: true, AttachStdin: true}
932	container, err := client.CreateContainer(CreateContainerOptions{Config: &config})
933	if container != nil {
934		t.Errorf("CreateContainer: expected <nil> container, got %#v.", container)
935	}
936	if err != ErrContainerAlreadyExists {
937		t.Errorf("CreateContainer: Wrong error type. Want %#v. Got %#v.", ErrContainerAlreadyExists, err)
938	}
939}
940
941func TestCreateContainerWithHostConfig(t *testing.T) {
942	t.Parallel()
943	fakeRT := &FakeRoundTripper{message: "{}", status: http.StatusOK}
944	client := newTestClient(fakeRT)
945	config := Config{}
946	hostConfig := HostConfig{PublishAllPorts: true}
947	opts := CreateContainerOptions{Name: "TestCreateContainerWithHostConfig", Config: &config, HostConfig: &hostConfig}
948	_, err := client.CreateContainer(opts)
949	if err != nil {
950		t.Fatal(err)
951	}
952	req := fakeRT.requests[0]
953	var gotBody map[string]interface{}
954	err = json.NewDecoder(req.Body).Decode(&gotBody)
955	if err != nil {
956		t.Fatal(err)
957	}
958	if _, ok := gotBody["HostConfig"]; !ok {
959		t.Errorf("CreateContainer: wrong body. HostConfig was not serialized")
960	}
961}
962
963func TestUpdateContainer(t *testing.T) {
964	t.Parallel()
965	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
966	client := newTestClient(fakeRT)
967	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
968	update := UpdateContainerOptions{Memory: 12345, CpusetMems: "0,1"}
969	err := client.UpdateContainer(id, update)
970	if err != nil {
971		t.Fatal(err)
972	}
973	req := fakeRT.requests[0]
974	if req.Method != "POST" {
975		t.Errorf("UpdateContainer: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
976	}
977	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/update"))
978	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
979		t.Errorf("UpdateContainer: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
980	}
981	expectedContentType := "application/json"
982	if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
983		t.Errorf("UpdateContainer: Wrong content-type in request. Want %q. Got %q.", expectedContentType, contentType)
984	}
985	var out UpdateContainerOptions
986	if err := json.NewDecoder(req.Body).Decode(&out); err != nil {
987		t.Fatal(err)
988	}
989	if !reflect.DeepEqual(out, update) {
990		t.Errorf("UpdateContainer: wrong body, got: %#v, want %#v", out, update)
991	}
992}
993
994func TestStartContainer(t *testing.T) {
995	t.Parallel()
996	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
997	client := newTestClient(fakeRT)
998	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
999	err := client.StartContainer(id, &HostConfig{})
1000	if err != nil {
1001		t.Fatal(err)
1002	}
1003	req := fakeRT.requests[0]
1004	if req.Method != "POST" {
1005		t.Errorf("StartContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1006	}
1007	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/start"))
1008	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1009		t.Errorf("StartContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1010	}
1011	expectedContentType := "application/json"
1012	if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
1013		t.Errorf("StartContainer(%q): Wrong content-type in request. Want %q. Got %q.", id, expectedContentType, contentType)
1014	}
1015}
1016
1017func TestStartContainerHostConfigAPI124(t *testing.T) {
1018	t.Parallel()
1019	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
1020	client := newTestClient(fakeRT)
1021	client.serverAPIVersion = apiVersion124
1022	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1023	err := client.StartContainer(id, &HostConfig{})
1024	if err != nil {
1025		t.Fatal(err)
1026	}
1027	req := fakeRT.requests[0]
1028	if req.Method != "POST" {
1029		t.Errorf("StartContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1030	}
1031	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/start"))
1032	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1033		t.Errorf("StartContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1034	}
1035	notAcceptedContentType := "application/json"
1036	if contentType := req.Header.Get("Content-Type"); contentType == notAcceptedContentType {
1037		t.Errorf("StartContainer(%q): Unepected %q Content-Type in request.", id, contentType)
1038	}
1039	if req.Body != nil {
1040		data, _ := ioutil.ReadAll(req.Body)
1041		t.Errorf("StartContainer(%q): Unexpected data sent: %s", id, data)
1042	}
1043}
1044
1045func TestStartContainerNilHostConfig(t *testing.T) {
1046	t.Parallel()
1047	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
1048	client := newTestClient(fakeRT)
1049	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1050	err := client.StartContainer(id, nil)
1051	if err != nil {
1052		t.Fatal(err)
1053	}
1054	req := fakeRT.requests[0]
1055	if req.Method != "POST" {
1056		t.Errorf("StartContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1057	}
1058	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/start"))
1059	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1060		t.Errorf("StartContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1061	}
1062	expectedContentType := "application/json"
1063	if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
1064		t.Errorf("StartContainer(%q): Wrong content-type in request. Want %q. Got %q.", id, expectedContentType, contentType)
1065	}
1066	var buf [4]byte
1067	req.Body.Read(buf[:])
1068	if string(buf[:]) != "null" {
1069		t.Errorf("Startcontainer(%q): Wrong body. Want null. Got %s", id, buf[:])
1070	}
1071}
1072
1073func TestStartContainerWithContext(t *testing.T) {
1074	t.Parallel()
1075	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
1076	client := newTestClient(fakeRT)
1077	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1078
1079	ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
1080	defer cancel()
1081
1082	startError := make(chan error)
1083	go func() {
1084		startError <- client.StartContainerWithContext(id, &HostConfig{}, ctx)
1085	}()
1086	select {
1087	case err := <-startError:
1088		if err != nil {
1089			t.Fatal(err)
1090		}
1091		req := fakeRT.requests[0]
1092		if req.Method != "POST" {
1093			t.Errorf("StartContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1094		}
1095		expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/start"))
1096		if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1097			t.Errorf("StartContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1098		}
1099		expectedContentType := "application/json"
1100		if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
1101			t.Errorf("StartContainer(%q): Wrong content-type in request. Want %q. Got %q.", id, expectedContentType, contentType)
1102		}
1103	case <-ctx.Done():
1104		// Context was canceled unexpectedly. Report the same.
1105		t.Fatalf("Context canceled when waiting for start container response: %v", ctx.Err())
1106	}
1107}
1108
1109func TestStartContainerNotFound(t *testing.T) {
1110	t.Parallel()
1111	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1112	err := client.StartContainer("a2344", &HostConfig{})
1113	expected := &NoSuchContainer{ID: "a2344", Err: err.(*NoSuchContainer).Err}
1114	if !reflect.DeepEqual(err, expected) {
1115		t.Errorf("StartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1116	}
1117}
1118
1119func TestStartContainerAlreadyRunning(t *testing.T) {
1120	t.Parallel()
1121	client := newTestClient(&FakeRoundTripper{message: "container already running", status: http.StatusNotModified})
1122	err := client.StartContainer("a2334", &HostConfig{})
1123	expected := &ContainerAlreadyRunning{ID: "a2334"}
1124	if !reflect.DeepEqual(err, expected) {
1125		t.Errorf("StartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1126	}
1127}
1128
1129func TestStopContainer(t *testing.T) {
1130	t.Parallel()
1131	fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
1132	client := newTestClient(fakeRT)
1133	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1134	err := client.StopContainer(id, 10)
1135	if err != nil {
1136		t.Fatal(err)
1137	}
1138	req := fakeRT.requests[0]
1139	if req.Method != "POST" {
1140		t.Errorf("StopContainer(%q, 10): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1141	}
1142	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/stop"))
1143	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1144		t.Errorf("StopContainer(%q, 10): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1145	}
1146}
1147
1148func TestStopContainerWithContext(t *testing.T) {
1149	t.Parallel()
1150	fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
1151	client := newTestClient(fakeRT)
1152	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1153
1154	ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
1155	defer cancel()
1156
1157	stopError := make(chan error)
1158	go func() {
1159		stopError <- client.StopContainerWithContext(id, 10, ctx)
1160	}()
1161	select {
1162	case err := <-stopError:
1163		if err != nil {
1164			t.Fatal(err)
1165		}
1166		req := fakeRT.requests[0]
1167		if req.Method != "POST" {
1168			t.Errorf("StopContainer(%q, 10): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1169		}
1170		expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/stop"))
1171		if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1172			t.Errorf("StopContainer(%q, 10): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1173		}
1174	case <-ctx.Done():
1175		// Context was canceled unexpectedly. Report the same.
1176		t.Fatalf("Context canceled when waiting for stop container response: %v", ctx.Err())
1177	}
1178}
1179
1180func TestStopContainerNotFound(t *testing.T) {
1181	t.Parallel()
1182	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1183	err := client.StopContainer("a2334", 10)
1184	expected := &NoSuchContainer{ID: "a2334"}
1185	if !reflect.DeepEqual(err, expected) {
1186		t.Errorf("StopContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1187	}
1188}
1189
1190func TestStopContainerNotRunning(t *testing.T) {
1191	t.Parallel()
1192	client := newTestClient(&FakeRoundTripper{message: "container not running", status: http.StatusNotModified})
1193	err := client.StopContainer("a2334", 10)
1194	expected := &ContainerNotRunning{ID: "a2334"}
1195	if !reflect.DeepEqual(err, expected) {
1196		t.Errorf("StopContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1197	}
1198}
1199
1200func TestRestartContainer(t *testing.T) {
1201	t.Parallel()
1202	fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
1203	client := newTestClient(fakeRT)
1204	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1205	err := client.RestartContainer(id, 10)
1206	if err != nil {
1207		t.Fatal(err)
1208	}
1209	req := fakeRT.requests[0]
1210	if req.Method != "POST" {
1211		t.Errorf("RestartContainer(%q, 10): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1212	}
1213	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/restart"))
1214	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1215		t.Errorf("RestartContainer(%q, 10): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1216	}
1217}
1218
1219func TestRestartContainerNotFound(t *testing.T) {
1220	t.Parallel()
1221	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1222	err := client.RestartContainer("a2334", 10)
1223	expected := &NoSuchContainer{ID: "a2334"}
1224	if !reflect.DeepEqual(err, expected) {
1225		t.Errorf("RestartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1226	}
1227}
1228
1229func TestPauseContainer(t *testing.T) {
1230	t.Parallel()
1231	fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
1232	client := newTestClient(fakeRT)
1233	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1234	err := client.PauseContainer(id)
1235	if err != nil {
1236		t.Fatal(err)
1237	}
1238	req := fakeRT.requests[0]
1239	if req.Method != "POST" {
1240		t.Errorf("PauseContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1241	}
1242	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/pause"))
1243	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1244		t.Errorf("PauseContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1245	}
1246}
1247
1248func TestPauseContainerNotFound(t *testing.T) {
1249	t.Parallel()
1250	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1251	err := client.PauseContainer("a2334")
1252	expected := &NoSuchContainer{ID: "a2334"}
1253	if !reflect.DeepEqual(err, expected) {
1254		t.Errorf("PauseContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1255	}
1256}
1257
1258func TestUnpauseContainer(t *testing.T) {
1259	t.Parallel()
1260	fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
1261	client := newTestClient(fakeRT)
1262	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1263	err := client.UnpauseContainer(id)
1264	if err != nil {
1265		t.Fatal(err)
1266	}
1267	req := fakeRT.requests[0]
1268	if req.Method != "POST" {
1269		t.Errorf("PauseContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1270	}
1271	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/unpause"))
1272	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1273		t.Errorf("PauseContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1274	}
1275}
1276
1277func TestUnpauseContainerNotFound(t *testing.T) {
1278	t.Parallel()
1279	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1280	err := client.UnpauseContainer("a2334")
1281	expected := &NoSuchContainer{ID: "a2334"}
1282	if !reflect.DeepEqual(err, expected) {
1283		t.Errorf("PauseContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1284	}
1285}
1286
1287func TestKillContainer(t *testing.T) {
1288	t.Parallel()
1289	fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
1290	client := newTestClient(fakeRT)
1291	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1292	err := client.KillContainer(KillContainerOptions{ID: id})
1293	if err != nil {
1294		t.Fatal(err)
1295	}
1296	req := fakeRT.requests[0]
1297	if req.Method != "POST" {
1298		t.Errorf("KillContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1299	}
1300	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/kill"))
1301	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1302		t.Errorf("KillContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1303	}
1304}
1305
1306func TestKillContainerSignal(t *testing.T) {
1307	t.Parallel()
1308	fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
1309	client := newTestClient(fakeRT)
1310	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1311	err := client.KillContainer(KillContainerOptions{ID: id, Signal: SIGTERM})
1312	if err != nil {
1313		t.Fatal(err)
1314	}
1315	req := fakeRT.requests[0]
1316	if req.Method != "POST" {
1317		t.Errorf("KillContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1318	}
1319	if signal := req.URL.Query().Get("signal"); signal != "15" {
1320		t.Errorf("KillContainer(%q): Wrong query string in request. Want %q. Got %q.", id, "15", signal)
1321	}
1322}
1323
1324func TestKillContainerNotFound(t *testing.T) {
1325	t.Parallel()
1326	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1327	err := client.KillContainer(KillContainerOptions{ID: "a2334"})
1328	expected := &NoSuchContainer{ID: "a2334"}
1329	if !reflect.DeepEqual(err, expected) {
1330		t.Errorf("KillContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1331	}
1332}
1333
1334func TestKillContainerNotRunning(t *testing.T) {
1335	t.Parallel()
1336	id := "abcd1234567890"
1337	msg := fmt.Sprintf("Cannot kill container: %[1]s: Container %[1]s is not running", id)
1338	client := newTestClient(&FakeRoundTripper{message: msg, status: http.StatusConflict})
1339	err := client.KillContainer(KillContainerOptions{ID: id})
1340	expected := &ContainerNotRunning{ID: id}
1341	if !reflect.DeepEqual(err, expected) {
1342		t.Errorf("KillContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1343	}
1344}
1345
1346func TestRemoveContainer(t *testing.T) {
1347	t.Parallel()
1348	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
1349	client := newTestClient(fakeRT)
1350	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1351	opts := RemoveContainerOptions{ID: id}
1352	err := client.RemoveContainer(opts)
1353	if err != nil {
1354		t.Fatal(err)
1355	}
1356	req := fakeRT.requests[0]
1357	if req.Method != "DELETE" {
1358		t.Errorf("RemoveContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "DELETE", req.Method)
1359	}
1360	expectedURL, _ := url.Parse(client.getURL("/containers/" + id))
1361	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1362		t.Errorf("RemoveContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1363	}
1364}
1365
1366func TestRemoveContainerRemoveVolumes(t *testing.T) {
1367	t.Parallel()
1368	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
1369	client := newTestClient(fakeRT)
1370	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1371	opts := RemoveContainerOptions{ID: id, RemoveVolumes: true}
1372	err := client.RemoveContainer(opts)
1373	if err != nil {
1374		t.Fatal(err)
1375	}
1376	req := fakeRT.requests[0]
1377	params := map[string][]string(req.URL.Query())
1378	expected := map[string][]string{"v": {"1"}}
1379	if !reflect.DeepEqual(params, expected) {
1380		t.Errorf("RemoveContainer(%q): wrong parameters. Want %#v. Got %#v.", id, expected, params)
1381	}
1382}
1383
1384func TestRemoveContainerNotFound(t *testing.T) {
1385	t.Parallel()
1386	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1387	err := client.RemoveContainer(RemoveContainerOptions{ID: "a2334"})
1388	expected := &NoSuchContainer{ID: "a2334"}
1389	if !reflect.DeepEqual(err, expected) {
1390		t.Errorf("RemoveContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1391	}
1392}
1393
1394func TestResizeContainerTTY(t *testing.T) {
1395	t.Parallel()
1396	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
1397	client := newTestClient(fakeRT)
1398	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1399	err := client.ResizeContainerTTY(id, 40, 80)
1400	if err != nil {
1401		t.Fatal(err)
1402	}
1403	req := fakeRT.requests[0]
1404	if req.Method != "POST" {
1405		t.Errorf("ResizeContainerTTY(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1406	}
1407	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/resize"))
1408	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1409		t.Errorf("ResizeContainerTTY(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1410	}
1411	got := map[string][]string(req.URL.Query())
1412	expectedParams := map[string][]string{
1413		"w": {"80"},
1414		"h": {"40"},
1415	}
1416	if !reflect.DeepEqual(got, expectedParams) {
1417		t.Errorf("Expected %#v, got %#v.", expectedParams, got)
1418	}
1419}
1420
1421func TestWaitContainer(t *testing.T) {
1422	t.Parallel()
1423	fakeRT := &FakeRoundTripper{message: `{"StatusCode": 56}`, status: http.StatusOK}
1424	client := newTestClient(fakeRT)
1425	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1426	status, err := client.WaitContainer(id)
1427	if err != nil {
1428		t.Fatal(err)
1429	}
1430	if status != 56 {
1431		t.Errorf("WaitContainer(%q): wrong return. Want 56. Got %d.", id, status)
1432	}
1433	req := fakeRT.requests[0]
1434	if req.Method != "POST" {
1435		t.Errorf("WaitContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1436	}
1437	expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/wait"))
1438	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1439		t.Errorf("WaitContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1440	}
1441}
1442
1443func TestWaitContainerWithContext(t *testing.T) {
1444	t.Parallel()
1445	fakeRT := &FakeRoundTripper{message: `{"StatusCode": 56}`, status: http.StatusOK}
1446	client := newTestClient(fakeRT)
1447	id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
1448
1449	ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
1450	defer cancel()
1451
1452	var status int
1453	waitError := make(chan error)
1454	go func() {
1455		var err error
1456		status, err = client.WaitContainerWithContext(id, ctx)
1457		waitError <- err
1458	}()
1459	select {
1460	case err := <-waitError:
1461		if err != nil {
1462			t.Fatal(err)
1463		}
1464		if status != 56 {
1465			t.Errorf("WaitContainer(%q): wrong return. Want 56. Got %d.", id, status)
1466		}
1467		req := fakeRT.requests[0]
1468		if req.Method != "POST" {
1469			t.Errorf("WaitContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method)
1470		}
1471		expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/wait"))
1472		if gotPath := req.URL.Path; gotPath != expectedURL.Path {
1473			t.Errorf("WaitContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
1474		}
1475	case <-ctx.Done():
1476		// Context was canceled unexpectedly. Report the same.
1477		t.Fatalf("Context canceled when waiting for wait container response: %v", ctx.Err())
1478	}
1479}
1480
1481func TestWaitContainerNotFound(t *testing.T) {
1482	t.Parallel()
1483	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1484	_, err := client.WaitContainer("a2334")
1485	expected := &NoSuchContainer{ID: "a2334"}
1486	if !reflect.DeepEqual(err, expected) {
1487		t.Errorf("WaitContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1488	}
1489}
1490
1491func TestCommitContainer(t *testing.T) {
1492	t.Parallel()
1493	response := `{"Id":"596069db4bf5"}`
1494	client := newTestClient(&FakeRoundTripper{message: response, status: http.StatusOK})
1495	id := "596069db4bf5"
1496	image, err := client.CommitContainer(CommitContainerOptions{})
1497	if err != nil {
1498		t.Fatal(err)
1499	}
1500	if image.ID != id {
1501		t.Errorf("CommitContainer: Wrong image id. Want %q. Got %q.", id, image.ID)
1502	}
1503}
1504
1505func TestCommitContainerParams(t *testing.T) {
1506	t.Parallel()
1507	cfg := Config{Memory: 67108864}
1508	json, _ := json.Marshal(&cfg)
1509	var tests = []struct {
1510		input  CommitContainerOptions
1511		params map[string][]string
1512		body   []byte
1513	}{
1514		{CommitContainerOptions{}, map[string][]string{}, nil},
1515		{CommitContainerOptions{Container: "44c004db4b17"}, map[string][]string{"container": {"44c004db4b17"}}, nil},
1516		{
1517			CommitContainerOptions{Container: "44c004db4b17", Repository: "tsuru/python", Message: "something"},
1518			map[string][]string{"container": {"44c004db4b17"}, "repo": {"tsuru/python"}, "comment": {"something"}},
1519			nil,
1520		},
1521		{
1522			CommitContainerOptions{Container: "44c004db4b17", Run: &cfg},
1523			map[string][]string{"container": {"44c004db4b17"}},
1524			json,
1525		},
1526	}
1527	fakeRT := &FakeRoundTripper{message: "{}", status: http.StatusOK}
1528	client := newTestClient(fakeRT)
1529	u, _ := url.Parse(client.getURL("/commit"))
1530	for _, tt := range tests {
1531		if _, err := client.CommitContainer(tt.input); err != nil {
1532			t.Error(err)
1533		}
1534		got := map[string][]string(fakeRT.requests[0].URL.Query())
1535		if !reflect.DeepEqual(got, tt.params) {
1536			t.Errorf("Expected %#v, got %#v.", tt.params, got)
1537		}
1538		if path := fakeRT.requests[0].URL.Path; path != u.Path {
1539			t.Errorf("Wrong path on request. Want %q. Got %q.", u.Path, path)
1540		}
1541		if meth := fakeRT.requests[0].Method; meth != "POST" {
1542			t.Errorf("Wrong HTTP method. Want POST. Got %s.", meth)
1543		}
1544		if tt.body != nil {
1545			if requestBody, err := ioutil.ReadAll(fakeRT.requests[0].Body); err == nil {
1546				if !bytes.Equal(requestBody, tt.body) {
1547					t.Errorf("Expected body %#v, got %#v", tt.body, requestBody)
1548				}
1549			} else {
1550				t.Errorf("Error reading request body: %#v", err)
1551			}
1552		}
1553		fakeRT.Reset()
1554	}
1555}
1556
1557func TestCommitContainerFailure(t *testing.T) {
1558	t.Parallel()
1559	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusInternalServerError})
1560	_, err := client.CommitContainer(CommitContainerOptions{})
1561	if err == nil {
1562		t.Error("Expected non-nil error, got <nil>.")
1563	}
1564}
1565
1566func TestCommitContainerNotFound(t *testing.T) {
1567	t.Parallel()
1568	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
1569	_, err := client.CommitContainer(CommitContainerOptions{})
1570	expected := &NoSuchContainer{ID: ""}
1571	if !reflect.DeepEqual(err, expected) {
1572		t.Errorf("CommitContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
1573	}
1574}
1575
1576func TestAttachToContainerLogs(t *testing.T) {
1577	t.Parallel()
1578	var req http.Request
1579	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1580		w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 19})
1581		w.Write([]byte("something happened!"))
1582		req = *r
1583	}))
1584	defer server.Close()
1585	client, _ := NewClient(server.URL)
1586	client.SkipServerVersionCheck = true
1587	var buf bytes.Buffer
1588	opts := AttachToContainerOptions{
1589		Container:    "a123456",
1590		OutputStream: &buf,
1591		Stdout:       true,
1592		Stderr:       true,
1593		Logs:         true,
1594	}
1595	err := client.AttachToContainer(opts)
1596	if err != nil {
1597		t.Fatal(err)
1598	}
1599	expected := "something happened!"
1600	if buf.String() != expected {
1601		t.Errorf("AttachToContainer for logs: wrong output. Want %q. Got %q.", expected, buf.String())
1602	}
1603	if req.Method != "POST" {
1604		t.Errorf("AttachToContainer: wrong HTTP method. Want POST. Got %s.", req.Method)
1605	}
1606	u, _ := url.Parse(client.getURL("/containers/a123456/attach"))
1607	if req.URL.Path != u.Path {
1608		t.Errorf("AttachToContainer for logs: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path)
1609	}
1610	expectedQs := map[string][]string{
1611		"logs":   {"1"},
1612		"stdout": {"1"},
1613		"stderr": {"1"},
1614	}
1615	got := map[string][]string(req.URL.Query())
1616	if !reflect.DeepEqual(got, expectedQs) {
1617		t.Errorf("AttachToContainer: wrong query string. Want %#v. Got %#v.", expectedQs, got)
1618	}
1619}
1620
1621func TestAttachToContainer(t *testing.T) {
1622	t.Parallel()
1623	var reader = strings.NewReader("send value")
1624	var req http.Request
1625	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1626		w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5})
1627		w.Write([]byte("hello"))
1628		req = *r
1629	}))
1630	defer server.Close()
1631	client, _ := NewClient(server.URL)
1632	client.SkipServerVersionCheck = true
1633	var stdout, stderr bytes.Buffer
1634	opts := AttachToContainerOptions{
1635		Container:    "a123456",
1636		OutputStream: &stdout,
1637		ErrorStream:  &stderr,
1638		InputStream:  reader,
1639		Stdin:        true,
1640		Stdout:       true,
1641		Stderr:       true,
1642		Stream:       true,
1643		RawTerminal:  true,
1644	}
1645	err := client.AttachToContainer(opts)
1646	if err != nil {
1647		t.Fatal(err)
1648	}
1649	expected := map[string][]string{
1650		"stdin":  {"1"},
1651		"stdout": {"1"},
1652		"stderr": {"1"},
1653		"stream": {"1"},
1654	}
1655	got := map[string][]string(req.URL.Query())
1656	if !reflect.DeepEqual(got, expected) {
1657		t.Errorf("AttachToContainer: wrong query string. Want %#v. Got %#v.", expected, got)
1658	}
1659}
1660
1661func TestAttachToContainerSentinel(t *testing.T) {
1662	t.Parallel()
1663	var reader = strings.NewReader("send value")
1664	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1665		w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5})
1666		w.Write([]byte("hello"))
1667	}))
1668	defer server.Close()
1669	client, _ := NewClient(server.URL)
1670	client.SkipServerVersionCheck = true
1671	var stdout, stderr bytes.Buffer
1672	success := make(chan struct{})
1673	opts := AttachToContainerOptions{
1674		Container:    "a123456",
1675		OutputStream: &stdout,
1676		ErrorStream:  &stderr,
1677		InputStream:  reader,
1678		Stdin:        true,
1679		Stdout:       true,
1680		Stderr:       true,
1681		Stream:       true,
1682		RawTerminal:  true,
1683		Success:      success,
1684	}
1685	errCh := make(chan error)
1686	go func() {
1687		errCh <- client.AttachToContainer(opts)
1688	}()
1689	success <- <-success
1690	if err := <-errCh; err != nil {
1691		t.Error(err)
1692	}
1693}
1694
1695func TestAttachToContainerNilStdout(t *testing.T) {
1696	t.Parallel()
1697	var reader = strings.NewReader("send value")
1698	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1699		w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5})
1700		w.Write([]byte("hello"))
1701	}))
1702	defer server.Close()
1703	client, _ := NewClient(server.URL)
1704	client.SkipServerVersionCheck = true
1705	var stderr bytes.Buffer
1706	opts := AttachToContainerOptions{
1707		Container:    "a123456",
1708		OutputStream: nil,
1709		ErrorStream:  &stderr,
1710		InputStream:  reader,
1711		Stdin:        true,
1712		Stdout:       true,
1713		Stderr:       true,
1714		Stream:       true,
1715		RawTerminal:  true,
1716	}
1717	err := client.AttachToContainer(opts)
1718	if err != nil {
1719		t.Fatal(err)
1720	}
1721}
1722
1723func TestAttachToContainerNilStderr(t *testing.T) {
1724	t.Parallel()
1725	var reader = strings.NewReader("send value")
1726	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1727		w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5})
1728		w.Write([]byte("hello"))
1729	}))
1730	defer server.Close()
1731	client, _ := NewClient(server.URL)
1732	client.SkipServerVersionCheck = true
1733	var stdout bytes.Buffer
1734	opts := AttachToContainerOptions{
1735		Container:    "a123456",
1736		OutputStream: &stdout,
1737		InputStream:  reader,
1738		Stdin:        true,
1739		Stdout:       true,
1740		Stderr:       true,
1741		Stream:       true,
1742		RawTerminal:  true,
1743	}
1744	err := client.AttachToContainer(opts)
1745	if err != nil {
1746		t.Fatal(err)
1747	}
1748}
1749
1750func TestAttachToContainerStdinOnly(t *testing.T) {
1751	t.Parallel()
1752	var reader = strings.NewReader("send value")
1753	serverFinished := make(chan struct{})
1754	clientFinished := make(chan struct{})
1755	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1756		w.WriteHeader(http.StatusOK)
1757		hj, ok := w.(http.Hijacker)
1758		if !ok {
1759			t.Fatal("cannot hijack server connection")
1760		}
1761		conn, _, err := hj.Hijack()
1762		if err != nil {
1763			t.Fatal(err)
1764		}
1765		// wait for client to indicate it's finished
1766		<-clientFinished
1767		// inform test that the server has finished
1768		close(serverFinished)
1769		conn.Close()
1770	}))
1771	defer server.Close()
1772	client, _ := NewClient(server.URL)
1773	client.SkipServerVersionCheck = true
1774	success := make(chan struct{})
1775	opts := AttachToContainerOptions{
1776		Container:   "a123456",
1777		InputStream: reader,
1778		Stdin:       true,
1779		Stdout:      false,
1780		Stderr:      false,
1781		Stream:      true,
1782		RawTerminal: false,
1783		Success:     success,
1784	}
1785	go func() {
1786		if err := client.AttachToContainer(opts); err != nil {
1787			t.Error(err)
1788		}
1789		// client's attach session is over
1790		close(clientFinished)
1791	}()
1792	success <- <-success
1793	// wait for server to finish handling attach
1794	<-serverFinished
1795}
1796
1797func TestAttachToContainerRawTerminalFalse(t *testing.T) {
1798	t.Parallel()
1799	input := strings.NewReader("send value")
1800	var req http.Request
1801	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1802		req = *r
1803		w.WriteHeader(http.StatusOK)
1804		hj, ok := w.(http.Hijacker)
1805		if !ok {
1806			t.Fatal("cannot hijack server connection")
1807		}
1808		conn, _, err := hj.Hijack()
1809		if err != nil {
1810			t.Fatal(err)
1811		}
1812		conn.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5})
1813		conn.Write([]byte("hello"))
1814		conn.Write([]byte{2, 0, 0, 0, 0, 0, 0, 6})
1815		conn.Write([]byte("hello!"))
1816		time.Sleep(10 * time.Millisecond)
1817		conn.Close()
1818	}))
1819	client, _ := NewClient(server.URL)
1820	client.SkipServerVersionCheck = true
1821	var stdout, stderr bytes.Buffer
1822	opts := AttachToContainerOptions{
1823		Container:    "a123456",
1824		OutputStream: &stdout,
1825		ErrorStream:  &stderr,
1826		InputStream:  input,
1827		Stdin:        true,
1828		Stdout:       true,
1829		Stderr:       true,
1830		Stream:       true,
1831		RawTerminal:  false,
1832	}
1833	client.AttachToContainer(opts)
1834	expected := map[string][]string{
1835		"stdin":  {"1"},
1836		"stdout": {"1"},
1837		"stderr": {"1"},
1838		"stream": {"1"},
1839	}
1840	got := map[string][]string(req.URL.Query())
1841	server.Close()
1842	if !reflect.DeepEqual(got, expected) {
1843		t.Errorf("AttachToContainer: wrong query string. Want %#v. Got %#v.", expected, got)
1844	}
1845	if stdout.String() != "hello" {
1846		t.Errorf("AttachToContainer: wrong content written to stdout. Want %q. Got %q.", "hello", stdout.String())
1847	}
1848	if stderr.String() != "hello!" {
1849		t.Errorf("AttachToContainer: wrong content written to stderr. Want %q. Got %q.", "hello!", stderr.String())
1850	}
1851}
1852
1853func TestAttachToContainerWithoutContainer(t *testing.T) {
1854	t.Parallel()
1855	var client Client
1856	err := client.AttachToContainer(AttachToContainerOptions{})
1857	expected := &NoSuchContainer{ID: ""}
1858	if !reflect.DeepEqual(err, expected) {
1859		t.Errorf("AttachToContainer: wrong error. Want %#v. Got %#v.", expected, err)
1860	}
1861}
1862
1863func TestLogs(t *testing.T) {
1864	t.Parallel()
1865	var req http.Request
1866	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1867		prefix := []byte{1, 0, 0, 0, 0, 0, 0, 19}
1868		w.Write(prefix)
1869		w.Write([]byte("something happened!"))
1870		req = *r
1871	}))
1872	defer server.Close()
1873	client, _ := NewClient(server.URL)
1874	client.SkipServerVersionCheck = true
1875	var buf bytes.Buffer
1876	opts := LogsOptions{
1877		Container:    "a123456",
1878		OutputStream: &buf,
1879		Follow:       true,
1880		Stdout:       true,
1881		Stderr:       true,
1882		Timestamps:   true,
1883	}
1884	err := client.Logs(opts)
1885	if err != nil {
1886		t.Fatal(err)
1887	}
1888	expected := "something happened!"
1889	if buf.String() != expected {
1890		t.Errorf("Logs: wrong output. Want %q. Got %q.", expected, buf.String())
1891	}
1892	if req.Method != "GET" {
1893		t.Errorf("Logs: wrong HTTP method. Want GET. Got %s.", req.Method)
1894	}
1895	u, _ := url.Parse(client.getURL("/containers/a123456/logs"))
1896	if req.URL.Path != u.Path {
1897		t.Errorf("AttachToContainer for logs: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path)
1898	}
1899	expectedQs := map[string][]string{
1900		"follow":     {"1"},
1901		"stdout":     {"1"},
1902		"stderr":     {"1"},
1903		"timestamps": {"1"},
1904		"tail":       {"all"},
1905	}
1906	got := map[string][]string(req.URL.Query())
1907	if !reflect.DeepEqual(got, expectedQs) {
1908		t.Errorf("Logs: wrong query string. Want %#v. Got %#v.", expectedQs, got)
1909	}
1910}
1911
1912func TestLogsNilStdoutDoesntFail(t *testing.T) {
1913	t.Parallel()
1914	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1915		prefix := []byte{1, 0, 0, 0, 0, 0, 0, 19}
1916		w.Write(prefix)
1917		w.Write([]byte("something happened!"))
1918	}))
1919	defer server.Close()
1920	client, _ := NewClient(server.URL)
1921	client.SkipServerVersionCheck = true
1922	opts := LogsOptions{
1923		Container:  "a123456",
1924		Follow:     true,
1925		Stdout:     true,
1926		Stderr:     true,
1927		Timestamps: true,
1928	}
1929	err := client.Logs(opts)
1930	if err != nil {
1931		t.Fatal(err)
1932	}
1933}
1934
1935func TestLogsNilStderrDoesntFail(t *testing.T) {
1936	t.Parallel()
1937	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1938		prefix := []byte{2, 0, 0, 0, 0, 0, 0, 19}
1939		w.Write(prefix)
1940		w.Write([]byte("something happened!"))
1941	}))
1942	defer server.Close()
1943	client, _ := NewClient(server.URL)
1944	client.SkipServerVersionCheck = true
1945	opts := LogsOptions{
1946		Container:  "a123456",
1947		Follow:     true,
1948		Stdout:     true,
1949		Stderr:     true,
1950		Timestamps: true,
1951	}
1952	err := client.Logs(opts)
1953	if err != nil {
1954		t.Fatal(err)
1955	}
1956}
1957
1958func TestLogsSpecifyingTail(t *testing.T) {
1959	t.Parallel()
1960	var req http.Request
1961	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1962		prefix := []byte{1, 0, 0, 0, 0, 0, 0, 19}
1963		w.Write(prefix)
1964		w.Write([]byte("something happened!"))
1965		req = *r
1966	}))
1967	defer server.Close()
1968	client, _ := NewClient(server.URL)
1969	client.SkipServerVersionCheck = true
1970	var buf bytes.Buffer
1971	opts := LogsOptions{
1972		Container:    "a123456",
1973		OutputStream: &buf,
1974		Follow:       true,
1975		Stdout:       true,
1976		Stderr:       true,
1977		Timestamps:   true,
1978		Tail:         "100",
1979	}
1980	err := client.Logs(opts)
1981	if err != nil {
1982		t.Fatal(err)
1983	}
1984	expected := "something happened!"
1985	if buf.String() != expected {
1986		t.Errorf("Logs: wrong output. Want %q. Got %q.", expected, buf.String())
1987	}
1988	if req.Method != "GET" {
1989		t.Errorf("Logs: wrong HTTP method. Want GET. Got %s.", req.Method)
1990	}
1991	u, _ := url.Parse(client.getURL("/containers/a123456/logs"))
1992	if req.URL.Path != u.Path {
1993		t.Errorf("AttachToContainer for logs: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path)
1994	}
1995	expectedQs := map[string][]string{
1996		"follow":     {"1"},
1997		"stdout":     {"1"},
1998		"stderr":     {"1"},
1999		"timestamps": {"1"},
2000		"tail":       {"100"},
2001	}
2002	got := map[string][]string(req.URL.Query())
2003	if !reflect.DeepEqual(got, expectedQs) {
2004		t.Errorf("Logs: wrong query string. Want %#v. Got %#v.", expectedQs, got)
2005	}
2006}
2007
2008func TestLogsRawTerminal(t *testing.T) {
2009	t.Parallel()
2010	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2011		w.Write([]byte("something happened!"))
2012	}))
2013	defer server.Close()
2014	client, _ := NewClient(server.URL)
2015	client.SkipServerVersionCheck = true
2016	var buf bytes.Buffer
2017	opts := LogsOptions{
2018		Container:    "a123456",
2019		OutputStream: &buf,
2020		Follow:       true,
2021		RawTerminal:  true,
2022		Stdout:       true,
2023		Stderr:       true,
2024		Timestamps:   true,
2025		Tail:         "100",
2026	}
2027	err := client.Logs(opts)
2028	if err != nil {
2029		t.Fatal(err)
2030	}
2031	expected := "something happened!"
2032	if buf.String() != expected {
2033		t.Errorf("Logs: wrong output. Want %q. Got %q.", expected, buf.String())
2034	}
2035}
2036
2037func TestLogsNoContainer(t *testing.T) {
2038	t.Parallel()
2039	var client Client
2040	err := client.Logs(LogsOptions{})
2041	expected := &NoSuchContainer{ID: ""}
2042	if !reflect.DeepEqual(err, expected) {
2043		t.Errorf("AttachToContainer: wrong error. Want %#v. Got %#v.", expected, err)
2044	}
2045}
2046
2047func TestNoSuchContainerError(t *testing.T) {
2048	t.Parallel()
2049	var err = &NoSuchContainer{ID: "i345"}
2050	expected := "No such container: i345"
2051	if got := err.Error(); got != expected {
2052		t.Errorf("NoSuchContainer: wrong message. Want %q. Got %q.", expected, got)
2053	}
2054}
2055
2056func TestNoSuchContainerErrorMessage(t *testing.T) {
2057	t.Parallel()
2058	var err = &NoSuchContainer{ID: "i345", Err: errors.New("some advanced error info")}
2059	expected := "some advanced error info"
2060	if got := err.Error(); got != expected {
2061		t.Errorf("NoSuchContainer: wrong message. Want %q. Got %q.", expected, got)
2062	}
2063}
2064
2065func TestExportContainer(t *testing.T) {
2066	t.Parallel()
2067	content := "exported container tar content"
2068	out := stdoutMock{bytes.NewBufferString(content)}
2069	client := newTestClient(&FakeRoundTripper{status: http.StatusOK})
2070	opts := ExportContainerOptions{ID: "4fa6e0f0c678", OutputStream: out}
2071	err := client.ExportContainer(opts)
2072	if err != nil {
2073		t.Errorf("ExportContainer: caugh error %#v while exporting container, expected nil", err.Error())
2074	}
2075	if out.String() != content {
2076		t.Errorf("ExportContainer: wrong stdout. Want %#v. Got %#v.", content, out.String())
2077	}
2078}
2079
2080func runStreamConnServer(t *testing.T, network, laddr string, listening chan<- string, done chan<- int, containerID string) {
2081	defer close(done)
2082	l, err := net.Listen(network, laddr)
2083	if err != nil {
2084		t.Errorf("Listen(%q, %q) failed: %v", network, laddr, err)
2085		listening <- "<nil>"
2086		return
2087	}
2088	defer l.Close()
2089	listening <- l.Addr().String()
2090	c, err := l.Accept()
2091	if err != nil {
2092		t.Logf("Accept failed: %v", err)
2093		return
2094	}
2095	defer c.Close()
2096	breader := bufio.NewReader(c)
2097	req, err := http.ReadRequest(breader)
2098	if err != nil {
2099		t.Fatal(err)
2100	}
2101	if path := "/containers/" + containerID + "/export"; req.URL.Path != path {
2102		t.Errorf("wrong path. Want %q. Got %q", path, req.URL.Path)
2103		return
2104	}
2105	c.Write([]byte("HTTP/1.1 200 OK\n\nexported container tar content"))
2106}
2107
2108func tempfile(filename string) string {
2109	return os.TempDir() + "/" + filename + "." + strconv.Itoa(os.Getpid())
2110}
2111
2112func TestExportContainerNoId(t *testing.T) {
2113	t.Parallel()
2114	client := Client{}
2115	out := stdoutMock{bytes.NewBufferString("")}
2116	err := client.ExportContainer(ExportContainerOptions{OutputStream: out})
2117	e, ok := err.(*NoSuchContainer)
2118	if !ok {
2119		t.Errorf("ExportContainer: wrong error. Want NoSuchContainer. Got %#v.", e)
2120	}
2121	if e.ID != "" {
2122		t.Errorf("ExportContainer: wrong ID. Want %q. Got %q", "", e.ID)
2123	}
2124}
2125
2126func TestUploadToContainer(t *testing.T) {
2127	t.Parallel()
2128	content := "File content"
2129	in := stdinMock{bytes.NewBufferString(content)}
2130	fakeRT := &FakeRoundTripper{status: http.StatusOK}
2131	client := newTestClient(fakeRT)
2132	opts := UploadToContainerOptions{
2133		Path:        "abc",
2134		InputStream: in,
2135	}
2136	err := client.UploadToContainer("a123456", opts)
2137	if err != nil {
2138		t.Errorf("UploadToContainer: caught error %#v while uploading archive to container, expected nil", err)
2139	}
2140
2141	req := fakeRT.requests[0]
2142
2143	if req.Method != "PUT" {
2144		t.Errorf("UploadToContainer{Path:abc}: Wrong HTTP method.  Want PUT. Got %s", req.Method)
2145	}
2146
2147	if pathParam := req.URL.Query().Get("path"); pathParam != "abc" {
2148		t.Errorf("ListImages({Path:abc}): Wrong parameter. Want path=abc.  Got path=%s", pathParam)
2149	}
2150
2151}
2152
2153func TestDownloadFromContainer(t *testing.T) {
2154	t.Parallel()
2155	filecontent := "File content"
2156	client := newTestClient(&FakeRoundTripper{message: filecontent, status: http.StatusOK})
2157
2158	var out bytes.Buffer
2159	opts := DownloadFromContainerOptions{
2160		OutputStream: &out,
2161	}
2162	err := client.DownloadFromContainer("a123456", opts)
2163	if err != nil {
2164		t.Errorf("DownloadFromContainer: caught error %#v while downloading from container, expected nil", err.Error())
2165	}
2166	if out.String() != filecontent {
2167		t.Errorf("DownloadFromContainer: wrong stdout. Want %#v. Got %#v.", filecontent, out.String())
2168	}
2169}
2170
2171func TestCopyFromContainer(t *testing.T) {
2172	t.Parallel()
2173	content := "File content"
2174	out := stdoutMock{bytes.NewBufferString(content)}
2175	client := newTestClient(&FakeRoundTripper{status: http.StatusOK})
2176	opts := CopyFromContainerOptions{
2177		Container:    "a123456",
2178		OutputStream: &out,
2179	}
2180	err := client.CopyFromContainer(opts)
2181	if err != nil {
2182		t.Errorf("CopyFromContainer: caught error %#v while copying from container, expected nil", err.Error())
2183	}
2184	if out.String() != content {
2185		t.Errorf("CopyFromContainer: wrong stdout. Want %#v. Got %#v.", content, out.String())
2186	}
2187}
2188
2189func TestCopyFromContainerEmptyContainer(t *testing.T) {
2190	t.Parallel()
2191	client := newTestClient(&FakeRoundTripper{status: http.StatusOK})
2192	err := client.CopyFromContainer(CopyFromContainerOptions{})
2193	_, ok := err.(*NoSuchContainer)
2194	if !ok {
2195		t.Errorf("CopyFromContainer: invalid error returned. Want NoSuchContainer, got %#v.", err)
2196	}
2197}
2198
2199func TestCopyFromContainerDockerAPI124(t *testing.T) {
2200	t.Parallel()
2201	client := newTestClient(&FakeRoundTripper{status: http.StatusOK})
2202	client.serverAPIVersion = apiVersion124
2203	opts := CopyFromContainerOptions{
2204		Container: "a123456",
2205	}
2206	err := client.CopyFromContainer(opts)
2207	if err == nil {
2208		t.Fatal("got unexpected <nil> error")
2209	}
2210	expectedMsg := "go-dockerclient: CopyFromContainer is no longer available in Docker >= 1.12, use DownloadFromContainer instead"
2211	if err.Error() != expectedMsg {
2212		t.Errorf("wrong error message\nWant %q\nGot  %q", expectedMsg, err.Error())
2213	}
2214}
2215
2216func TestPassingNameOptToCreateContainerReturnsItInContainer(t *testing.T) {
2217	t.Parallel()
2218	jsonContainer := `{
2219             "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
2220	     "Warnings": []
2221}`
2222	fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
2223	client := newTestClient(fakeRT)
2224	config := Config{AttachStdout: true, AttachStdin: true}
2225	opts := CreateContainerOptions{Name: "TestCreateContainer", Config: &config}
2226	container, err := client.CreateContainer(opts)
2227	if err != nil {
2228		t.Fatal(err)
2229	}
2230	if container.Name != "TestCreateContainer" {
2231		t.Errorf("Container name expected to be TestCreateContainer, was %s", container.Name)
2232	}
2233}
2234
2235func TestAlwaysRestart(t *testing.T) {
2236	t.Parallel()
2237	policy := AlwaysRestart()
2238	if policy.Name != "always" {
2239		t.Errorf("AlwaysRestart(): wrong policy name. Want %q. Got %q", "always", policy.Name)
2240	}
2241	if policy.MaximumRetryCount != 0 {
2242		t.Errorf("AlwaysRestart(): wrong MaximumRetryCount. Want 0. Got %d", policy.MaximumRetryCount)
2243	}
2244}
2245
2246func TestRestartOnFailure(t *testing.T) {
2247	t.Parallel()
2248	const retry = 5
2249	policy := RestartOnFailure(retry)
2250	if policy.Name != "on-failure" {
2251		t.Errorf("RestartOnFailure(%d): wrong policy name. Want %q. Got %q", retry, "on-failure", policy.Name)
2252	}
2253	if policy.MaximumRetryCount != retry {
2254		t.Errorf("RestartOnFailure(%d): wrong MaximumRetryCount. Want %d. Got %d", retry, retry, policy.MaximumRetryCount)
2255	}
2256}
2257
2258func TestRestartUnlessStopped(t *testing.T) {
2259	t.Parallel()
2260	policy := RestartUnlessStopped()
2261	if policy.Name != "unless-stopped" {
2262		t.Errorf("RestartUnlessStopped(): wrong policy name. Want %q. Got %q", "unless-stopped", policy.Name)
2263	}
2264	if policy.MaximumRetryCount != 0 {
2265		t.Errorf("RestartUnlessStopped(): wrong MaximumRetryCount. Want 0. Got %d", policy.MaximumRetryCount)
2266	}
2267}
2268
2269func TestNeverRestart(t *testing.T) {
2270	t.Parallel()
2271	policy := NeverRestart()
2272	if policy.Name != "no" {
2273		t.Errorf("NeverRestart(): wrong policy name. Want %q. Got %q", "always", policy.Name)
2274	}
2275	if policy.MaximumRetryCount != 0 {
2276		t.Errorf("NeverRestart(): wrong MaximumRetryCount. Want 0. Got %d", policy.MaximumRetryCount)
2277	}
2278}
2279
2280func TestTopContainer(t *testing.T) {
2281	t.Parallel()
2282	jsonTop := `{
2283  "Processes": [
2284    [
2285      "ubuntu",
2286      "3087",
2287      "815",
2288      "0",
2289      "01:44",
2290      "?",
2291      "00:00:00",
2292      "cmd1"
2293    ],
2294    [
2295      "root",
2296      "3158",
2297      "3087",
2298      "0",
2299      "01:44",
2300      "?",
2301      "00:00:01",
2302      "cmd2"
2303    ]
2304  ],
2305  "Titles": [
2306    "UID",
2307    "PID",
2308    "PPID",
2309    "C",
2310    "STIME",
2311    "TTY",
2312    "TIME",
2313    "CMD"
2314  ]
2315}`
2316	var expected TopResult
2317	err := json.Unmarshal([]byte(jsonTop), &expected)
2318	if err != nil {
2319		t.Fatal(err)
2320	}
2321	id := "4fa6e0f0"
2322	fakeRT := &FakeRoundTripper{message: jsonTop, status: http.StatusOK}
2323	client := newTestClient(fakeRT)
2324	processes, err := client.TopContainer(id, "")
2325	if err != nil {
2326		t.Fatal(err)
2327	}
2328	if !reflect.DeepEqual(processes, expected) {
2329		t.Errorf("TopContainer: Expected %#v. Got %#v.", expected, processes)
2330	}
2331	if len(processes.Processes) != 2 || len(processes.Processes[0]) != 8 ||
2332		processes.Processes[0][7] != "cmd1" {
2333		t.Errorf("TopContainer: Process list to include cmd1. Got %#v.", processes)
2334	}
2335	expectedURI := "/containers/" + id + "/top"
2336	if !strings.HasSuffix(fakeRT.requests[0].URL.String(), expectedURI) {
2337		t.Errorf("TopContainer: Expected URI to have %q. Got %q.", expectedURI, fakeRT.requests[0].URL.String())
2338	}
2339}
2340
2341func TestTopContainerNotFound(t *testing.T) {
2342	t.Parallel()
2343	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
2344	_, err := client.TopContainer("abef348", "")
2345	expected := &NoSuchContainer{ID: "abef348"}
2346	if !reflect.DeepEqual(err, expected) {
2347		t.Errorf("StopContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
2348	}
2349}
2350
2351func TestTopContainerWithPsArgs(t *testing.T) {
2352	t.Parallel()
2353	fakeRT := &FakeRoundTripper{message: "no such container", status: http.StatusNotFound}
2354	client := newTestClient(fakeRT)
2355	expectedErr := &NoSuchContainer{ID: "abef348"}
2356	if _, err := client.TopContainer("abef348", "aux"); !reflect.DeepEqual(expectedErr, err) {
2357		t.Errorf("TopContainer: Expected %v. Got %v.", expectedErr, err)
2358	}
2359	expectedURI := "/containers/abef348/top?ps_args=aux"
2360	if !strings.HasSuffix(fakeRT.requests[0].URL.String(), expectedURI) {
2361		t.Errorf("TopContainer: Expected URI to have %q. Got %q.", expectedURI, fakeRT.requests[0].URL.String())
2362	}
2363}
2364
2365func TestStats(t *testing.T) {
2366	t.Parallel()
2367	jsonStats1 := `{
2368       "read" : "2015-01-08T22:57:31.547920715Z",
2369       "network" : {
2370          "rx_dropped" : 0,
2371          "rx_bytes" : 648,
2372          "rx_errors" : 0,
2373          "tx_packets" : 8,
2374          "tx_dropped" : 0,
2375          "rx_packets" : 8,
2376          "tx_errors" : 0,
2377          "tx_bytes" : 648
2378       },
2379	   "networks" : {
2380		   "eth0":{
2381			   "rx_dropped" : 0,
2382			   "rx_bytes" : 648,
2383			   "rx_errors" : 0,
2384			   "tx_packets" : 8,
2385			   "tx_dropped" : 0,
2386			   "rx_packets" : 8,
2387			   "tx_errors" : 0,
2388			   "tx_bytes" : 648
2389		   }
2390	   },
2391       "memory_stats" : {
2392          "stats" : {
2393             "total_pgmajfault" : 0,
2394             "cache" : 0,
2395             "mapped_file" : 0,
2396             "total_inactive_file" : 0,
2397             "pgpgout" : 414,
2398             "rss" : 6537216,
2399             "total_mapped_file" : 0,
2400             "writeback" : 0,
2401             "unevictable" : 0,
2402             "pgpgin" : 477,
2403             "total_unevictable" : 0,
2404             "pgmajfault" : 0,
2405             "total_rss" : 6537216,
2406             "total_rss_huge" : 6291456,
2407             "total_writeback" : 0,
2408             "total_inactive_anon" : 0,
2409             "rss_huge" : 6291456,
2410	     "hierarchical_memory_limit": 189204833,
2411             "total_pgfault" : 964,
2412             "total_active_file" : 0,
2413             "active_anon" : 6537216,
2414             "total_active_anon" : 6537216,
2415             "total_pgpgout" : 414,
2416             "total_cache" : 0,
2417             "inactive_anon" : 0,
2418             "active_file" : 0,
2419             "pgfault" : 964,
2420             "inactive_file" : 0,
2421             "total_pgpgin" : 477,
2422             "swap" : 47312896,
2423             "hierarchical_memsw_limit" : 1610612736
2424          },
2425          "max_usage" : 6651904,
2426          "usage" : 6537216,
2427          "failcnt" : 0,
2428          "limit" : 67108864
2429       },
2430       "blkio_stats": {
2431          "io_service_bytes_recursive": [
2432             {
2433                "major": 8,
2434                "minor": 0,
2435                "op": "Read",
2436                "value": 428795731968
2437             },
2438             {
2439                "major": 8,
2440                "minor": 0,
2441                "op": "Write",
2442                "value": 388177920
2443             }
2444          ],
2445          "io_serviced_recursive": [
2446             {
2447                "major": 8,
2448                "minor": 0,
2449                "op": "Read",
2450                "value": 25994442
2451             },
2452             {
2453                "major": 8,
2454                "minor": 0,
2455                "op": "Write",
2456                "value": 1734
2457             }
2458          ],
2459          "io_queue_recursive": [],
2460          "io_service_time_recursive": [],
2461          "io_wait_time_recursive": [],
2462          "io_merged_recursive": [],
2463          "io_time_recursive": [],
2464          "sectors_recursive": []
2465       },
2466       "cpu_stats" : {
2467          "cpu_usage" : {
2468             "percpu_usage" : [
2469                16970827,
2470                1839451,
2471                7107380,
2472                10571290
2473             ],
2474             "usage_in_usermode" : 10000000,
2475             "total_usage" : 36488948,
2476             "usage_in_kernelmode" : 20000000
2477          },
2478          "system_cpu_usage" : 20091722000000000,
2479		  "online_cpus": 4
2480       },
2481       "precpu_stats" : {
2482          "cpu_usage" : {
2483             "percpu_usage" : [
2484                16970827,
2485                1839451,
2486                7107380,
2487                10571290
2488             ],
2489             "usage_in_usermode" : 10000000,
2490             "total_usage" : 36488948,
2491             "usage_in_kernelmode" : 20000000
2492          },
2493          "system_cpu_usage" : 20091722000000000,
2494		  "online_cpus": 4
2495       }
2496    }`
2497	// 1 second later, cache is 100
2498	jsonStats2 := `{
2499       "read" : "2015-01-08T22:57:32.547920715Z",
2500	   "networks" : {
2501		   "eth0":{
2502			   "rx_dropped" : 0,
2503			   "rx_bytes" : 648,
2504			   "rx_errors" : 0,
2505			   "tx_packets" : 8,
2506			   "tx_dropped" : 0,
2507			   "rx_packets" : 8,
2508			   "tx_errors" : 0,
2509			   "tx_bytes" : 648
2510		   }
2511	   },
2512	   "memory_stats" : {
2513          "stats" : {
2514             "total_pgmajfault" : 0,
2515             "cache" : 100,
2516             "mapped_file" : 0,
2517             "total_inactive_file" : 0,
2518             "pgpgout" : 414,
2519             "rss" : 6537216,
2520             "total_mapped_file" : 0,
2521             "writeback" : 0,
2522             "unevictable" : 0,
2523             "pgpgin" : 477,
2524             "total_unevictable" : 0,
2525             "pgmajfault" : 0,
2526             "total_rss" : 6537216,
2527             "total_rss_huge" : 6291456,
2528             "total_writeback" : 0,
2529             "total_inactive_anon" : 0,
2530             "rss_huge" : 6291456,
2531             "total_pgfault" : 964,
2532             "total_active_file" : 0,
2533             "active_anon" : 6537216,
2534             "total_active_anon" : 6537216,
2535             "total_pgpgout" : 414,
2536             "total_cache" : 0,
2537             "inactive_anon" : 0,
2538             "active_file" : 0,
2539             "pgfault" : 964,
2540             "inactive_file" : 0,
2541             "total_pgpgin" : 477,
2542             "swap" : 47312896,
2543             "hierarchical_memsw_limit" : 1610612736
2544          },
2545          "max_usage" : 6651904,
2546          "usage" : 6537216,
2547          "failcnt" : 0,
2548          "limit" : 67108864
2549       },
2550       "blkio_stats": {
2551          "io_service_bytes_recursive": [
2552             {
2553                "major": 8,
2554                "minor": 0,
2555                "op": "Read",
2556                "value": 428795731968
2557             },
2558             {
2559                "major": 8,
2560                "minor": 0,
2561                "op": "Write",
2562                "value": 388177920
2563             }
2564          ],
2565          "io_serviced_recursive": [
2566             {
2567                "major": 8,
2568                "minor": 0,
2569                "op": "Read",
2570                "value": 25994442
2571             },
2572             {
2573                "major": 8,
2574                "minor": 0,
2575                "op": "Write",
2576                "value": 1734
2577             }
2578          ],
2579          "io_queue_recursive": [],
2580          "io_service_time_recursive": [],
2581          "io_wait_time_recursive": [],
2582          "io_merged_recursive": [],
2583          "io_time_recursive": [],
2584          "sectors_recursive": []
2585       },
2586       "cpu_stats" : {
2587          "cpu_usage" : {
2588             "percpu_usage" : [
2589                16970827,
2590                1839451,
2591                7107380,
2592                10571290
2593             ],
2594             "usage_in_usermode" : 10000000,
2595             "total_usage" : 36488948,
2596             "usage_in_kernelmode" : 20000000
2597          },
2598          "system_cpu_usage" : 20091722000000000,
2599		  "online_cpus": 4
2600       },
2601       "precpu_stats" : {
2602          "cpu_usage" : {
2603             "percpu_usage" : [
2604                16970827,
2605                1839451,
2606                7107380,
2607                10571290
2608             ],
2609             "usage_in_usermode" : 10000000,
2610             "total_usage" : 36488948,
2611             "usage_in_kernelmode" : 20000000
2612          },
2613          "system_cpu_usage" : 20091722000000000,
2614		  "online_cpus": 4
2615       }
2616    }`
2617	var expected1 Stats
2618	var expected2 Stats
2619	err := json.Unmarshal([]byte(jsonStats1), &expected1)
2620	if err != nil {
2621		t.Fatal(err)
2622	}
2623	err = json.Unmarshal([]byte(jsonStats2), &expected2)
2624	if err != nil {
2625		t.Fatal(err)
2626	}
2627	id := "4fa6e0f0"
2628
2629	var req http.Request
2630	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2631		w.Header().Set("Content-Type", "application/json")
2632		w.Write([]byte(jsonStats1))
2633		w.Write([]byte(jsonStats2))
2634		req = *r
2635	}))
2636	defer server.Close()
2637	client, _ := NewClient(server.URL)
2638	client.SkipServerVersionCheck = true
2639	errC := make(chan error, 1)
2640	statsC := make(chan *Stats)
2641	done := make(chan bool)
2642	defer close(done)
2643	go func() {
2644		errC <- client.Stats(StatsOptions{ID: id, Stats: statsC, Stream: true, Done: done})
2645		close(errC)
2646	}()
2647	var resultStats []*Stats
2648	for {
2649		stats, ok := <-statsC
2650		if !ok {
2651			break
2652		}
2653		resultStats = append(resultStats, stats)
2654	}
2655	err = <-errC
2656	if err != nil {
2657		t.Fatal(err)
2658	}
2659	if len(resultStats) != 2 {
2660		t.Fatalf("Stats: Expected 2 results. Got %d.", len(resultStats))
2661	}
2662	if !reflect.DeepEqual(resultStats[0], &expected1) {
2663		t.Errorf("Stats: Expected:\n%+v\nGot:\n%+v", expected1, resultStats[0])
2664	}
2665	if !reflect.DeepEqual(resultStats[1], &expected2) {
2666		t.Errorf("Stats: Expected:\n%+v\nGot:\n%+v", expected2, resultStats[1])
2667	}
2668	if req.Method != "GET" {
2669		t.Errorf("Stats: wrong HTTP method. Want GET. Got %s.", req.Method)
2670	}
2671	u, _ := url.Parse(client.getURL("/containers/" + id + "/stats"))
2672	if req.URL.Path != u.Path {
2673		t.Errorf("Stats: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path)
2674	}
2675}
2676
2677func TestStatsContainerNotFound(t *testing.T) {
2678	t.Parallel()
2679	client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
2680	statsC := make(chan *Stats)
2681	done := make(chan bool)
2682	defer close(done)
2683	err := client.Stats(StatsOptions{ID: "abef348", Stats: statsC, Stream: true, Done: done})
2684	expected := &NoSuchContainer{ID: "abef348"}
2685	if !reflect.DeepEqual(err, expected) {
2686		t.Errorf("Stats: Wrong error returned. Want %#v. Got %#v.", expected, err)
2687	}
2688}
2689
2690func TestRenameContainer(t *testing.T) {
2691	t.Parallel()
2692	fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
2693	client := newTestClient(fakeRT)
2694	opts := RenameContainerOptions{ID: "something_old", Name: "something_new"}
2695	err := client.RenameContainer(opts)
2696	if err != nil {
2697		t.Fatal(err)
2698	}
2699	req := fakeRT.requests[0]
2700	if req.Method != "POST" {
2701		t.Errorf("RenameContainer: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
2702	}
2703	expectedURL, _ := url.Parse(client.getURL("/containers/something_old/rename?name=something_new"))
2704	if gotPath := req.URL.Path; gotPath != expectedURL.Path {
2705		t.Errorf("RenameContainer: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
2706	}
2707	expectedValues := expectedURL.Query()["name"]
2708	actualValues := req.URL.Query()["name"]
2709	if len(actualValues) != 1 || expectedValues[0] != actualValues[0] {
2710		t.Errorf("RenameContainer: Wrong params in request. Want %q. Got %q.", expectedValues, actualValues)
2711	}
2712}
2713
2714// sleepyRoundTripper implements the http.RoundTripper interface. It sleeps
2715// for the 'sleep' duration and then returns an error for RoundTrip method.
2716type sleepyRoudTripper struct {
2717	sleepDuration time.Duration
2718}
2719
2720func (rt *sleepyRoudTripper) RoundTrip(r *http.Request) (*http.Response, error) {
2721	time.Sleep(rt.sleepDuration)
2722	return nil, fmt.Errorf("Can't complete round trip")
2723}
2724
2725func TestInspectContainerWhenContextTimesOut(t *testing.T) {
2726	t.Parallel()
2727	rt := sleepyRoudTripper{sleepDuration: 200 * time.Millisecond}
2728
2729	client := newTestClient(&rt)
2730
2731	ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
2732	defer cancel()
2733
2734	_, err := client.InspectContainerWithContext("id", ctx)
2735	if err != context.DeadlineExceeded {
2736		t.Errorf("Expected 'DeadlineExceededError', got: %v", err)
2737	}
2738}
2739
2740func TestStartContainerWhenContextTimesOut(t *testing.T) {
2741	t.Parallel()
2742	rt := sleepyRoudTripper{sleepDuration: 200 * time.Millisecond}
2743
2744	client := newTestClient(&rt)
2745
2746	ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
2747	defer cancel()
2748
2749	err := client.StartContainerWithContext("id", nil, ctx)
2750	if err != context.DeadlineExceeded {
2751		t.Errorf("Expected 'DeadlineExceededError', got: %v", err)
2752	}
2753}
2754
2755func TestStopContainerWhenContextTimesOut(t *testing.T) {
2756	t.Parallel()
2757	rt := sleepyRoudTripper{sleepDuration: 300 * time.Millisecond}
2758
2759	client := newTestClient(&rt)
2760
2761	ctx, cancel := context.WithTimeout(context.TODO(), 50*time.Millisecond)
2762	defer cancel()
2763
2764	err := client.StopContainerWithContext("id", 10, ctx)
2765	if err != context.DeadlineExceeded {
2766		t.Errorf("Expected 'DeadlineExceededError', got: %v", err)
2767	}
2768}
2769
2770func TestWaitContainerWhenContextTimesOut(t *testing.T) {
2771	t.Parallel()
2772	rt := sleepyRoudTripper{sleepDuration: 200 * time.Millisecond}
2773
2774	client := newTestClient(&rt)
2775
2776	ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
2777	defer cancel()
2778
2779	_, err := client.WaitContainerWithContext("id", ctx)
2780	if err != context.DeadlineExceeded {
2781		t.Errorf("Expected 'DeadlineExceededError', got: %v", err)
2782	}
2783}
2784
2785func TestPruneContainers(t *testing.T) {
2786	t.Parallel()
2787	results := `{
2788		"ContainersDeleted": [
2789			"a", "b", "c"
2790		],
2791		"SpaceReclaimed": 123
2792	}`
2793
2794	expected := &PruneContainersResults{}
2795	err := json.Unmarshal([]byte(results), expected)
2796	if err != nil {
2797		t.Fatal(err)
2798	}
2799	client := newTestClient(&FakeRoundTripper{message: results, status: http.StatusOK})
2800	got, err := client.PruneContainers(PruneContainersOptions{})
2801	if err != nil {
2802		t.Fatal(err)
2803	}
2804	if !reflect.DeepEqual(got, expected) {
2805		t.Errorf("PruneContainers: Expected %#v. Got %#v.", expected, got)
2806	}
2807}
2808