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