github.com/yamamoto-febc/docker@v1.9.0/integration-cli/docker_utils.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net" 12 "net/http" 13 "net/http/httptest" 14 "net/http/httputil" 15 "net/url" 16 "os" 17 "os/exec" 18 "path" 19 "path/filepath" 20 "strconv" 21 "strings" 22 "time" 23 24 "github.com/docker/docker/api/types" 25 "github.com/docker/docker/opts" 26 "github.com/docker/docker/pkg/httputils" 27 "github.com/docker/docker/pkg/integration" 28 "github.com/docker/docker/pkg/ioutils" 29 "github.com/docker/docker/pkg/sockets" 30 "github.com/docker/docker/pkg/stringutils" 31 "github.com/docker/docker/pkg/tlsconfig" 32 "github.com/go-check/check" 33 ) 34 35 // Daemon represents a Docker daemon for the testing framework. 36 type Daemon struct { 37 // Defaults to "daemon" 38 // Useful to set to --daemon or -d for checking backwards compatibility 39 Command string 40 GlobalFlags []string 41 42 id string 43 c *check.C 44 logFile *os.File 45 folder string 46 root string 47 stdin io.WriteCloser 48 stdout, stderr io.ReadCloser 49 cmd *exec.Cmd 50 storageDriver string 51 execDriver string 52 wait chan error 53 userlandProxy bool 54 useDefaultHost bool 55 useDefaultTLSHost bool 56 } 57 58 type clientConfig struct { 59 transport *http.Transport 60 scheme string 61 addr string 62 } 63 64 // NewDaemon returns a Daemon instance to be used for testing. 65 // This will create a directory such as d123456789 in the folder specified by $DEST. 66 // The daemon will not automatically start. 67 func NewDaemon(c *check.C) *Daemon { 68 dest := os.Getenv("DEST") 69 if dest == "" { 70 c.Fatal("Please set the DEST environment variable") 71 } 72 73 id := fmt.Sprintf("d%d", time.Now().UnixNano()%100000000) 74 dir := filepath.Join(dest, id) 75 daemonFolder, err := filepath.Abs(dir) 76 if err != nil { 77 c.Fatalf("Could not make %q an absolute path: %v", dir, err) 78 } 79 daemonRoot := filepath.Join(daemonFolder, "root") 80 81 if err := os.MkdirAll(daemonRoot, 0755); err != nil { 82 c.Fatalf("Could not create daemon root %q: %v", dir, err) 83 } 84 85 userlandProxy := true 86 if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" { 87 if val, err := strconv.ParseBool(env); err != nil { 88 userlandProxy = val 89 } 90 } 91 92 return &Daemon{ 93 Command: "daemon", 94 id: id, 95 c: c, 96 folder: daemonFolder, 97 root: daemonRoot, 98 storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"), 99 execDriver: os.Getenv("DOCKER_EXECDRIVER"), 100 userlandProxy: userlandProxy, 101 } 102 } 103 104 func (d *Daemon) getClientConfig() (*clientConfig, error) { 105 var ( 106 transport *http.Transport 107 scheme string 108 addr string 109 proto string 110 ) 111 if d.useDefaultTLSHost { 112 option := &tlsconfig.Options{ 113 CAFile: "fixtures/https/ca.pem", 114 CertFile: "fixtures/https/client-cert.pem", 115 KeyFile: "fixtures/https/client-key.pem", 116 } 117 tlsConfig, err := tlsconfig.Client(*option) 118 if err != nil { 119 return nil, err 120 } 121 transport = &http.Transport{ 122 TLSClientConfig: tlsConfig, 123 } 124 addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort) 125 scheme = "https" 126 proto = "tcp" 127 } else if d.useDefaultHost { 128 addr = opts.DefaultUnixSocket 129 proto = "unix" 130 scheme = "http" 131 transport = &http.Transport{} 132 } else { 133 addr = filepath.Join(d.folder, "docker.sock") 134 proto = "unix" 135 scheme = "http" 136 transport = &http.Transport{} 137 } 138 139 sockets.ConfigureTCPTransport(transport, proto, addr) 140 141 return &clientConfig{ 142 transport: transport, 143 scheme: scheme, 144 addr: addr, 145 }, nil 146 } 147 148 // Start will start the daemon and return once it is ready to receive requests. 149 // You can specify additional daemon flags. 150 func (d *Daemon) Start(arg ...string) error { 151 dockerBinary, err := exec.LookPath(dockerBinary) 152 if err != nil { 153 d.c.Fatalf("[%s] could not find docker binary in $PATH: %v", d.id, err) 154 } 155 156 args := append(d.GlobalFlags, 157 d.Command, 158 "--graph", d.root, 159 "--pidfile", fmt.Sprintf("%s/docker.pid", d.folder), 160 fmt.Sprintf("--userland-proxy=%t", d.userlandProxy), 161 ) 162 if !(d.useDefaultHost || d.useDefaultTLSHost) { 163 args = append(args, []string{"--host", d.sock()}...) 164 } 165 if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" { 166 args = append(args, []string{"--userns-remap", root}...) 167 } 168 169 // If we don't explicitly set the log-level or debug flag(-D) then 170 // turn on debug mode 171 foundIt := false 172 for _, a := range arg { 173 if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") { 174 foundIt = true 175 } 176 } 177 if !foundIt { 178 args = append(args, "--debug") 179 } 180 181 if d.storageDriver != "" { 182 args = append(args, "--storage-driver", d.storageDriver) 183 } 184 if d.execDriver != "" { 185 args = append(args, "--exec-driver", d.execDriver) 186 } 187 188 args = append(args, arg...) 189 d.cmd = exec.Command(dockerBinary, args...) 190 191 d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) 192 if err != nil { 193 d.c.Fatalf("[%s] Could not create %s/docker.log: %v", d.id, d.folder, err) 194 } 195 196 d.cmd.Stdout = d.logFile 197 d.cmd.Stderr = d.logFile 198 199 if err := d.cmd.Start(); err != nil { 200 return fmt.Errorf("[%s] could not start daemon container: %v", d.id, err) 201 } 202 203 wait := make(chan error) 204 205 go func() { 206 wait <- d.cmd.Wait() 207 d.c.Logf("[%s] exiting daemon", d.id) 208 close(wait) 209 }() 210 211 d.wait = wait 212 213 tick := time.Tick(500 * time.Millisecond) 214 // make sure daemon is ready to receive requests 215 startTime := time.Now().Unix() 216 for { 217 d.c.Logf("[%s] waiting for daemon to start", d.id) 218 if time.Now().Unix()-startTime > 5 { 219 // After 5 seconds, give up 220 return fmt.Errorf("[%s] Daemon exited and never started", d.id) 221 } 222 select { 223 case <-time.After(2 * time.Second): 224 return fmt.Errorf("[%s] timeout: daemon does not respond", d.id) 225 case <-tick: 226 clientConfig, err := d.getClientConfig() 227 if err != nil { 228 return err 229 } 230 231 client := &http.Client{ 232 Transport: clientConfig.transport, 233 } 234 235 req, err := http.NewRequest("GET", "/_ping", nil) 236 if err != nil { 237 d.c.Fatalf("[%s] could not create new request: %v", d.id, err) 238 } 239 req.URL.Host = clientConfig.addr 240 req.URL.Scheme = clientConfig.scheme 241 resp, err := client.Do(req) 242 if err != nil { 243 continue 244 } 245 if resp.StatusCode != http.StatusOK { 246 d.c.Logf("[%s] received status != 200 OK: %s", d.id, resp.Status) 247 } 248 d.c.Logf("[%s] daemon started", d.id) 249 d.root, err = d.queryRootDir() 250 if err != nil { 251 return fmt.Errorf("[%s] error querying daemon for root directory: %v", d.id, err) 252 } 253 return nil 254 } 255 } 256 } 257 258 // StartWithBusybox will first start the daemon with Daemon.Start() 259 // then save the busybox image from the main daemon and load it into this Daemon instance. 260 func (d *Daemon) StartWithBusybox(arg ...string) error { 261 if err := d.Start(arg...); err != nil { 262 return err 263 } 264 bb := filepath.Join(d.folder, "busybox.tar") 265 if _, err := os.Stat(bb); err != nil { 266 if !os.IsNotExist(err) { 267 return fmt.Errorf("unexpected error on busybox.tar stat: %v", err) 268 } 269 // saving busybox image from main daemon 270 if err := exec.Command(dockerBinary, "save", "--output", bb, "busybox:latest").Run(); err != nil { 271 return fmt.Errorf("could not save busybox image: %v", err) 272 } 273 } 274 // loading busybox image to this daemon 275 if _, err := d.Cmd("load", "--input", bb); err != nil { 276 return fmt.Errorf("could not load busybox image: %v", err) 277 } 278 if err := os.Remove(bb); err != nil { 279 d.c.Logf("Could not remove %s: %v", bb, err) 280 } 281 return nil 282 } 283 284 // Stop will send a SIGINT every second and wait for the daemon to stop. 285 // If it timeouts, a SIGKILL is sent. 286 // Stop will not delete the daemon directory. If a purged daemon is needed, 287 // instantiate a new one with NewDaemon. 288 func (d *Daemon) Stop() error { 289 if d.cmd == nil || d.wait == nil { 290 return errors.New("daemon not started") 291 } 292 293 defer func() { 294 d.logFile.Close() 295 d.cmd = nil 296 }() 297 298 i := 1 299 tick := time.Tick(time.Second) 300 301 if err := d.cmd.Process.Signal(os.Interrupt); err != nil { 302 return fmt.Errorf("could not send signal: %v", err) 303 } 304 out1: 305 for { 306 select { 307 case err := <-d.wait: 308 return err 309 case <-time.After(15 * time.Second): 310 // time for stopping jobs and run onShutdown hooks 311 d.c.Log("timeout") 312 break out1 313 } 314 } 315 316 out2: 317 for { 318 select { 319 case err := <-d.wait: 320 return err 321 case <-tick: 322 i++ 323 if i > 4 { 324 d.c.Logf("tried to interrupt daemon for %d times, now try to kill it", i) 325 break out2 326 } 327 d.c.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid) 328 if err := d.cmd.Process.Signal(os.Interrupt); err != nil { 329 return fmt.Errorf("could not send signal: %v", err) 330 } 331 } 332 } 333 334 if err := d.cmd.Process.Kill(); err != nil { 335 d.c.Logf("Could not kill daemon: %v", err) 336 return err 337 } 338 339 return nil 340 } 341 342 // Restart will restart the daemon by first stopping it and then starting it. 343 func (d *Daemon) Restart(arg ...string) error { 344 d.Stop() 345 return d.Start(arg...) 346 } 347 348 func (d *Daemon) queryRootDir() (string, error) { 349 // update daemon root by asking /info endpoint (to support user 350 // namespaced daemon with root remapped uid.gid directory) 351 clientConfig, err := d.getClientConfig() 352 if err != nil { 353 return "", err 354 } 355 356 client := &http.Client{ 357 Transport: clientConfig.transport, 358 } 359 360 req, err := http.NewRequest("GET", "/info", nil) 361 if err != nil { 362 return "", err 363 } 364 req.Header.Set("Content-Type", "application/json") 365 req.URL.Host = clientConfig.addr 366 req.URL.Scheme = clientConfig.scheme 367 368 resp, err := client.Do(req) 369 if err != nil { 370 return "", err 371 } 372 body := ioutils.NewReadCloserWrapper(resp.Body, func() error { 373 return resp.Body.Close() 374 }) 375 376 type Info struct { 377 DockerRootDir string 378 } 379 var b []byte 380 var i Info 381 b, err = readBody(body) 382 if err == nil && resp.StatusCode == 200 { 383 // read the docker root dir 384 if err = json.Unmarshal(b, &i); err == nil { 385 return i.DockerRootDir, nil 386 } 387 } 388 return "", err 389 } 390 391 func (d *Daemon) sock() string { 392 return fmt.Sprintf("unix://%s/docker.sock", d.folder) 393 } 394 395 // Cmd will execute a docker CLI command against this Daemon. 396 // Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version 397 func (d *Daemon) Cmd(name string, arg ...string) (string, error) { 398 args := []string{"--host", d.sock(), name} 399 args = append(args, arg...) 400 c := exec.Command(dockerBinary, args...) 401 b, err := c.CombinedOutput() 402 return string(b), err 403 } 404 405 // CmdWithArgs will execute a docker CLI command against a daemon with the 406 // given additional arguments 407 func (d *Daemon) CmdWithArgs(daemonArgs []string, name string, arg ...string) (string, error) { 408 args := append(daemonArgs, name) 409 args = append(args, arg...) 410 c := exec.Command(dockerBinary, args...) 411 b, err := c.CombinedOutput() 412 return string(b), err 413 } 414 415 // LogfileName returns the path the the daemon's log file 416 func (d *Daemon) LogfileName() string { 417 return d.logFile.Name() 418 } 419 420 func daemonHost() string { 421 daemonURLStr := "unix://" + opts.DefaultUnixSocket 422 if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" { 423 daemonURLStr = daemonHostVar 424 } 425 return daemonURLStr 426 } 427 428 func sockConn(timeout time.Duration) (net.Conn, error) { 429 daemon := daemonHost() 430 daemonURL, err := url.Parse(daemon) 431 if err != nil { 432 return nil, fmt.Errorf("could not parse url %q: %v", daemon, err) 433 } 434 435 var c net.Conn 436 switch daemonURL.Scheme { 437 case "unix": 438 return net.DialTimeout(daemonURL.Scheme, daemonURL.Path, timeout) 439 case "tcp": 440 return net.DialTimeout(daemonURL.Scheme, daemonURL.Host, timeout) 441 default: 442 return c, fmt.Errorf("unknown scheme %v (%s)", daemonURL.Scheme, daemon) 443 } 444 } 445 446 func sockRequest(method, endpoint string, data interface{}) (int, []byte, error) { 447 jsonData := bytes.NewBuffer(nil) 448 if err := json.NewEncoder(jsonData).Encode(data); err != nil { 449 return -1, nil, err 450 } 451 452 res, body, err := sockRequestRaw(method, endpoint, jsonData, "application/json") 453 if err != nil { 454 return -1, nil, err 455 } 456 b, err := readBody(body) 457 return res.StatusCode, b, err 458 } 459 460 func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) { 461 req, client, err := newRequestClient(method, endpoint, data, ct) 462 if err != nil { 463 return nil, nil, err 464 } 465 466 resp, err := client.Do(req) 467 if err != nil { 468 client.Close() 469 return nil, nil, err 470 } 471 body := ioutils.NewReadCloserWrapper(resp.Body, func() error { 472 defer resp.Body.Close() 473 return client.Close() 474 }) 475 476 return resp, body, nil 477 } 478 479 func sockRequestHijack(method, endpoint string, data io.Reader, ct string) (net.Conn, *bufio.Reader, error) { 480 req, client, err := newRequestClient(method, endpoint, data, ct) 481 if err != nil { 482 return nil, nil, err 483 } 484 485 client.Do(req) 486 conn, br := client.Hijack() 487 return conn, br, nil 488 } 489 490 func newRequestClient(method, endpoint string, data io.Reader, ct string) (*http.Request, *httputil.ClientConn, error) { 491 c, err := sockConn(time.Duration(10 * time.Second)) 492 if err != nil { 493 return nil, nil, fmt.Errorf("could not dial docker daemon: %v", err) 494 } 495 496 client := httputil.NewClientConn(c, nil) 497 498 req, err := http.NewRequest(method, endpoint, data) 499 if err != nil { 500 client.Close() 501 return nil, nil, fmt.Errorf("could not create new request: %v", err) 502 } 503 504 if ct != "" { 505 req.Header.Set("Content-Type", ct) 506 } 507 return req, client, nil 508 } 509 510 func readBody(b io.ReadCloser) ([]byte, error) { 511 defer b.Close() 512 return ioutil.ReadAll(b) 513 } 514 515 func deleteContainer(container string) error { 516 container = strings.TrimSpace(strings.Replace(container, "\n", " ", -1)) 517 rmArgs := strings.Split(fmt.Sprintf("rm -fv %v", container), " ") 518 exitCode, err := runCommand(exec.Command(dockerBinary, rmArgs...)) 519 // set error manually if not set 520 if exitCode != 0 && err == nil { 521 err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero") 522 } 523 524 return err 525 } 526 527 func getAllContainers() (string, error) { 528 getContainersCmd := exec.Command(dockerBinary, "ps", "-q", "-a") 529 out, exitCode, err := runCommandWithOutput(getContainersCmd) 530 if exitCode != 0 && err == nil { 531 err = fmt.Errorf("failed to get a list of containers: %v\n", out) 532 } 533 534 return out, err 535 } 536 537 func deleteAllContainers() error { 538 containers, err := getAllContainers() 539 if err != nil { 540 fmt.Println(containers) 541 return err 542 } 543 544 if err = deleteContainer(containers); err != nil { 545 return err 546 } 547 return nil 548 } 549 550 func deleteAllNetworks() error { 551 networks, err := getAllNetworks() 552 if err != nil { 553 return err 554 } 555 var errors []string 556 for _, n := range networks { 557 if n.Name != "bridge" { 558 status, b, err := sockRequest("DELETE", "/networks/"+n.Name, nil) 559 if err != nil { 560 errors = append(errors, err.Error()) 561 continue 562 } 563 if status != http.StatusNoContent { 564 errors = append(errors, fmt.Sprintf("error deleting network %s: %s", n.Name, string(b))) 565 } 566 } 567 } 568 if len(errors) > 0 { 569 return fmt.Errorf(strings.Join(errors, "\n")) 570 } 571 return nil 572 } 573 574 func getAllNetworks() ([]types.NetworkResource, error) { 575 var networks []types.NetworkResource 576 _, b, err := sockRequest("GET", "/networks", nil) 577 if err != nil { 578 return nil, err 579 } 580 if err := json.Unmarshal(b, &networks); err != nil { 581 return nil, err 582 } 583 return networks, nil 584 } 585 586 func deleteAllVolumes() error { 587 volumes, err := getAllVolumes() 588 if err != nil { 589 return err 590 } 591 var errors []string 592 for _, v := range volumes { 593 status, b, err := sockRequest("DELETE", "/volumes/"+v.Name, nil) 594 if err != nil { 595 errors = append(errors, err.Error()) 596 continue 597 } 598 if status != http.StatusNoContent { 599 errors = append(errors, fmt.Sprintf("error deleting volume %s: %s", v.Name, string(b))) 600 } 601 } 602 if len(errors) > 0 { 603 return fmt.Errorf(strings.Join(errors, "\n")) 604 } 605 return nil 606 } 607 608 func getAllVolumes() ([]*types.Volume, error) { 609 var volumes types.VolumesListResponse 610 _, b, err := sockRequest("GET", "/volumes", nil) 611 if err != nil { 612 return nil, err 613 } 614 if err := json.Unmarshal(b, &volumes); err != nil { 615 return nil, err 616 } 617 return volumes.Volumes, nil 618 } 619 620 var protectedImages = map[string]struct{}{} 621 622 func init() { 623 out, err := exec.Command(dockerBinary, "images").CombinedOutput() 624 if err != nil { 625 panic(err) 626 } 627 lines := strings.Split(string(out), "\n")[1:] 628 for _, l := range lines { 629 if l == "" { 630 continue 631 } 632 fields := strings.Fields(l) 633 imgTag := fields[0] + ":" + fields[1] 634 // just for case if we have dangling images in tested daemon 635 if imgTag != "<none>:<none>" { 636 protectedImages[imgTag] = struct{}{} 637 } 638 } 639 640 // Obtain the daemon platform so that it can be used by tests to make 641 // intelligent decisions about how to configure themselves, and validate 642 // that the target platform is valid. 643 res, _, err := sockRequestRaw("GET", "/version", nil, "application/json") 644 if err != nil || res == nil || (res != nil && res.StatusCode != http.StatusOK) { 645 panic(fmt.Errorf("Init failed to get version: %v. Res=%v", err.Error(), res)) 646 } 647 svrHeader, _ := httputils.ParseServerHeader(res.Header.Get("Server")) 648 daemonPlatform = svrHeader.OS 649 if daemonPlatform != "linux" && daemonPlatform != "windows" { 650 panic("Cannot run tests against platform: " + daemonPlatform) 651 } 652 } 653 654 func deleteAllImages() error { 655 out, err := exec.Command(dockerBinary, "images").CombinedOutput() 656 if err != nil { 657 return err 658 } 659 lines := strings.Split(string(out), "\n")[1:] 660 var imgs []string 661 for _, l := range lines { 662 if l == "" { 663 continue 664 } 665 fields := strings.Fields(l) 666 imgTag := fields[0] + ":" + fields[1] 667 if _, ok := protectedImages[imgTag]; !ok { 668 if fields[0] == "<none>" { 669 imgs = append(imgs, fields[2]) 670 continue 671 } 672 imgs = append(imgs, imgTag) 673 } 674 } 675 if len(imgs) == 0 { 676 return nil 677 } 678 args := append([]string{"rmi", "-f"}, imgs...) 679 if err := exec.Command(dockerBinary, args...).Run(); err != nil { 680 return err 681 } 682 return nil 683 } 684 685 func getPausedContainers() (string, error) { 686 getPausedContainersCmd := exec.Command(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") 687 out, exitCode, err := runCommandWithOutput(getPausedContainersCmd) 688 if exitCode != 0 && err == nil { 689 err = fmt.Errorf("failed to get a list of paused containers: %v\n", out) 690 } 691 692 return out, err 693 } 694 695 func getSliceOfPausedContainers() ([]string, error) { 696 out, err := getPausedContainers() 697 if err == nil { 698 if len(out) == 0 { 699 return nil, err 700 } 701 slice := strings.Split(strings.TrimSpace(out), "\n") 702 return slice, err 703 } 704 return []string{out}, err 705 } 706 707 func unpauseContainer(container string) error { 708 unpauseCmd := exec.Command(dockerBinary, "unpause", container) 709 exitCode, err := runCommand(unpauseCmd) 710 if exitCode != 0 && err == nil { 711 err = fmt.Errorf("failed to unpause container") 712 } 713 714 return nil 715 } 716 717 func unpauseAllContainers() error { 718 containers, err := getPausedContainers() 719 if err != nil { 720 fmt.Println(containers) 721 return err 722 } 723 724 containers = strings.Replace(containers, "\n", " ", -1) 725 containers = strings.Trim(containers, " ") 726 containerList := strings.Split(containers, " ") 727 728 for _, value := range containerList { 729 if err = unpauseContainer(value); err != nil { 730 return err 731 } 732 } 733 734 return nil 735 } 736 737 func deleteImages(images ...string) error { 738 args := []string{"rmi", "-f"} 739 args = append(args, images...) 740 rmiCmd := exec.Command(dockerBinary, args...) 741 exitCode, err := runCommand(rmiCmd) 742 // set error manually if not set 743 if exitCode != 0 && err == nil { 744 err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero") 745 } 746 return err 747 } 748 749 func imageExists(image string) error { 750 inspectCmd := exec.Command(dockerBinary, "inspect", image) 751 exitCode, err := runCommand(inspectCmd) 752 if exitCode != 0 && err == nil { 753 err = fmt.Errorf("couldn't find image %q", image) 754 } 755 return err 756 } 757 758 func pullImageIfNotExist(image string) error { 759 if err := imageExists(image); err != nil { 760 pullCmd := exec.Command(dockerBinary, "pull", image) 761 _, exitCode, err := runCommandWithOutput(pullCmd) 762 763 if err != nil || exitCode != 0 { 764 return fmt.Errorf("image %q wasn't found locally and it couldn't be pulled: %s", image, err) 765 } 766 } 767 return nil 768 } 769 770 func dockerCmdWithError(args ...string) (string, int, error) { 771 return integration.DockerCmdWithError(dockerBinary, args...) 772 } 773 774 func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) { 775 return integration.DockerCmdWithStdoutStderr(dockerBinary, c, args...) 776 } 777 778 func dockerCmd(c *check.C, args ...string) (string, int) { 779 return integration.DockerCmd(dockerBinary, c, args...) 780 } 781 782 // execute a docker command with a timeout 783 func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) { 784 return integration.DockerCmdWithTimeout(dockerBinary, timeout, args...) 785 } 786 787 // execute a docker command in a directory 788 func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error) { 789 return integration.DockerCmdInDir(dockerBinary, path, args...) 790 } 791 792 // execute a docker command in a directory with a timeout 793 func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) (string, int, error) { 794 return integration.DockerCmdInDirWithTimeout(dockerBinary, timeout, path, args...) 795 } 796 797 func findContainerIP(c *check.C, id string, network string) string { 798 out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", network), id) 799 return strings.Trim(out, " \r\n'") 800 } 801 802 func (d *Daemon) findContainerIP(id string) string { 803 out, err := d.Cmd("inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.bridge.IPAddress }}'"), id) 804 if err != nil { 805 d.c.Log(err) 806 } 807 return strings.Trim(out, " \r\n'") 808 } 809 810 func getContainerCount() (int, error) { 811 const containers = "Containers:" 812 813 cmd := exec.Command(dockerBinary, "info") 814 out, _, err := runCommandWithOutput(cmd) 815 if err != nil { 816 return 0, err 817 } 818 819 lines := strings.Split(out, "\n") 820 for _, line := range lines { 821 if strings.Contains(line, containers) { 822 output := strings.TrimSpace(line) 823 output = strings.TrimLeft(output, containers) 824 output = strings.Trim(output, " ") 825 containerCount, err := strconv.Atoi(output) 826 if err != nil { 827 return 0, err 828 } 829 return containerCount, nil 830 } 831 } 832 return 0, fmt.Errorf("couldn't find the Container count in the output") 833 } 834 835 // FakeContext creates directories that can be used as a build context 836 type FakeContext struct { 837 Dir string 838 } 839 840 // Add a file at a path, creating directories where necessary 841 func (f *FakeContext) Add(file, content string) error { 842 return f.addFile(file, []byte(content)) 843 } 844 845 func (f *FakeContext) addFile(file string, content []byte) error { 846 filepath := path.Join(f.Dir, file) 847 dirpath := path.Dir(filepath) 848 if dirpath != "." { 849 if err := os.MkdirAll(dirpath, 0755); err != nil { 850 return err 851 } 852 } 853 return ioutil.WriteFile(filepath, content, 0644) 854 855 } 856 857 // Delete a file at a path 858 func (f *FakeContext) Delete(file string) error { 859 filepath := path.Join(f.Dir, file) 860 return os.RemoveAll(filepath) 861 } 862 863 // Close deletes the context 864 func (f *FakeContext) Close() error { 865 return os.RemoveAll(f.Dir) 866 } 867 868 func fakeContextFromNewTempDir() (*FakeContext, error) { 869 tmp, err := ioutil.TempDir("", "fake-context") 870 if err != nil { 871 return nil, err 872 } 873 if err := os.Chmod(tmp, 0755); err != nil { 874 return nil, err 875 } 876 return fakeContextFromDir(tmp), nil 877 } 878 879 func fakeContextFromDir(dir string) *FakeContext { 880 return &FakeContext{dir} 881 } 882 883 func fakeContextWithFiles(files map[string]string) (*FakeContext, error) { 884 ctx, err := fakeContextFromNewTempDir() 885 if err != nil { 886 return nil, err 887 } 888 for file, content := range files { 889 if err := ctx.Add(file, content); err != nil { 890 ctx.Close() 891 return nil, err 892 } 893 } 894 return ctx, nil 895 } 896 897 func fakeContextAddDockerfile(ctx *FakeContext, dockerfile string) error { 898 if err := ctx.Add("Dockerfile", dockerfile); err != nil { 899 ctx.Close() 900 return err 901 } 902 return nil 903 } 904 905 func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) { 906 ctx, err := fakeContextWithFiles(files) 907 if err != nil { 908 return nil, err 909 } 910 if err := fakeContextAddDockerfile(ctx, dockerfile); err != nil { 911 return nil, err 912 } 913 return ctx, nil 914 } 915 916 // FakeStorage is a static file server. It might be running locally or remotely 917 // on test host. 918 type FakeStorage interface { 919 Close() error 920 URL() string 921 CtxDir() string 922 } 923 924 func fakeBinaryStorage(archives map[string]*bytes.Buffer) (FakeStorage, error) { 925 ctx, err := fakeContextFromNewTempDir() 926 if err != nil { 927 return nil, err 928 } 929 for name, content := range archives { 930 if err := ctx.addFile(name, content.Bytes()); err != nil { 931 return nil, err 932 } 933 } 934 return fakeStorageWithContext(ctx) 935 } 936 937 // fakeStorage returns either a local or remote (at daemon machine) file server 938 func fakeStorage(files map[string]string) (FakeStorage, error) { 939 ctx, err := fakeContextWithFiles(files) 940 if err != nil { 941 return nil, err 942 } 943 return fakeStorageWithContext(ctx) 944 } 945 946 // fakeStorageWithContext returns either a local or remote (at daemon machine) file server 947 func fakeStorageWithContext(ctx *FakeContext) (FakeStorage, error) { 948 if isLocalDaemon { 949 return newLocalFakeStorage(ctx) 950 } 951 return newRemoteFileServer(ctx) 952 } 953 954 // localFileStorage is a file storage on the running machine 955 type localFileStorage struct { 956 *FakeContext 957 *httptest.Server 958 } 959 960 func (s *localFileStorage) URL() string { 961 return s.Server.URL 962 } 963 964 func (s *localFileStorage) CtxDir() string { 965 return s.FakeContext.Dir 966 } 967 968 func (s *localFileStorage) Close() error { 969 defer s.Server.Close() 970 return s.FakeContext.Close() 971 } 972 973 func newLocalFakeStorage(ctx *FakeContext) (*localFileStorage, error) { 974 handler := http.FileServer(http.Dir(ctx.Dir)) 975 server := httptest.NewServer(handler) 976 return &localFileStorage{ 977 FakeContext: ctx, 978 Server: server, 979 }, nil 980 } 981 982 // remoteFileServer is a containerized static file server started on the remote 983 // testing machine to be used in URL-accepting docker build functionality. 984 type remoteFileServer struct { 985 host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712 986 container string 987 image string 988 ctx *FakeContext 989 } 990 991 func (f *remoteFileServer) URL() string { 992 u := url.URL{ 993 Scheme: "http", 994 Host: f.host} 995 return u.String() 996 } 997 998 func (f *remoteFileServer) CtxDir() string { 999 return f.ctx.Dir 1000 } 1001 1002 func (f *remoteFileServer) Close() error { 1003 defer func() { 1004 if f.ctx != nil { 1005 f.ctx.Close() 1006 } 1007 if f.image != "" { 1008 deleteImages(f.image) 1009 } 1010 }() 1011 if f.container == "" { 1012 return nil 1013 } 1014 return deleteContainer(f.container) 1015 } 1016 1017 func newRemoteFileServer(ctx *FakeContext) (*remoteFileServer, error) { 1018 var ( 1019 image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10))) 1020 container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10))) 1021 ) 1022 1023 // Build the image 1024 if err := fakeContextAddDockerfile(ctx, `FROM httpserver 1025 COPY . /static`); err != nil { 1026 return nil, fmt.Errorf("Cannot add Dockerfile to context: %v", err) 1027 } 1028 if _, err := buildImageFromContext(image, ctx, false); err != nil { 1029 return nil, fmt.Errorf("failed building file storage container image: %v", err) 1030 } 1031 1032 // Start the container 1033 runCmd := exec.Command(dockerBinary, "run", "-d", "-P", "--name", container, image) 1034 if out, ec, err := runCommandWithOutput(runCmd); err != nil { 1035 return nil, fmt.Errorf("failed to start file storage container. ec=%v\nout=%s\nerr=%v", ec, out, err) 1036 } 1037 1038 // Find out the system assigned port 1039 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "port", container, "80/tcp")) 1040 if err != nil { 1041 return nil, fmt.Errorf("failed to find container port: err=%v\nout=%s", err, out) 1042 } 1043 1044 fileserverHostPort := strings.Trim(out, "\n") 1045 _, port, err := net.SplitHostPort(fileserverHostPort) 1046 if err != nil { 1047 return nil, fmt.Errorf("unable to parse file server host:port: %v", err) 1048 } 1049 1050 dockerHostURL, err := url.Parse(daemonHost()) 1051 if err != nil { 1052 return nil, fmt.Errorf("unable to parse daemon host URL: %v", err) 1053 } 1054 1055 host, _, err := net.SplitHostPort(dockerHostURL.Host) 1056 if err != nil { 1057 return nil, fmt.Errorf("unable to parse docker daemon host:port: %v", err) 1058 } 1059 1060 return &remoteFileServer{ 1061 container: container, 1062 image: image, 1063 host: fmt.Sprintf("%s:%s", host, port), 1064 ctx: ctx}, nil 1065 } 1066 1067 func inspectFieldAndMarshall(name, field string, output interface{}) error { 1068 str, err := inspectFieldJSON(name, field) 1069 if err != nil { 1070 return err 1071 } 1072 1073 return json.Unmarshal([]byte(str), output) 1074 } 1075 1076 func inspectFilter(name, filter string) (string, error) { 1077 format := fmt.Sprintf("{{%s}}", filter) 1078 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 1079 out, exitCode, err := runCommandWithOutput(inspectCmd) 1080 if err != nil || exitCode != 0 { 1081 return "", fmt.Errorf("failed to inspect container %s: %s", name, out) 1082 } 1083 return strings.TrimSpace(out), nil 1084 } 1085 1086 func inspectField(name, field string) (string, error) { 1087 return inspectFilter(name, fmt.Sprintf(".%s", field)) 1088 } 1089 1090 func inspectFieldJSON(name, field string) (string, error) { 1091 return inspectFilter(name, fmt.Sprintf("json .%s", field)) 1092 } 1093 1094 func inspectFieldMap(name, path, field string) (string, error) { 1095 return inspectFilter(name, fmt.Sprintf("index .%s %q", path, field)) 1096 } 1097 1098 func inspectMountSourceField(name, destination string) (string, error) { 1099 m, err := inspectMountPoint(name, destination) 1100 if err != nil { 1101 return "", err 1102 } 1103 return m.Source, nil 1104 } 1105 1106 func inspectMountPoint(name, destination string) (types.MountPoint, error) { 1107 out, err := inspectFieldJSON(name, "Mounts") 1108 if err != nil { 1109 return types.MountPoint{}, err 1110 } 1111 1112 return inspectMountPointJSON(out, destination) 1113 } 1114 1115 var errMountNotFound = errors.New("mount point not found") 1116 1117 func inspectMountPointJSON(j, destination string) (types.MountPoint, error) { 1118 var mp []types.MountPoint 1119 if err := unmarshalJSON([]byte(j), &mp); err != nil { 1120 return types.MountPoint{}, err 1121 } 1122 1123 var m *types.MountPoint 1124 for _, c := range mp { 1125 if c.Destination == destination { 1126 m = &c 1127 break 1128 } 1129 } 1130 1131 if m == nil { 1132 return types.MountPoint{}, errMountNotFound 1133 } 1134 1135 return *m, nil 1136 } 1137 1138 func getIDByName(name string) (string, error) { 1139 return inspectField(name, "Id") 1140 } 1141 1142 // getContainerState returns the exit code of the container 1143 // and true if it's running 1144 // the exit code should be ignored if it's running 1145 func getContainerState(c *check.C, id string) (int, bool, error) { 1146 var ( 1147 exitStatus int 1148 running bool 1149 ) 1150 out, exitCode := dockerCmd(c, "inspect", "--format={{.State.Running}} {{.State.ExitCode}}", id) 1151 if exitCode != 0 { 1152 return 0, false, fmt.Errorf("%q doesn't exist: %s", id, out) 1153 } 1154 1155 out = strings.Trim(out, "\n") 1156 splitOutput := strings.Split(out, " ") 1157 if len(splitOutput) != 2 { 1158 return 0, false, fmt.Errorf("failed to get container state: output is broken") 1159 } 1160 if splitOutput[0] == "true" { 1161 running = true 1162 } 1163 if n, err := strconv.Atoi(splitOutput[1]); err == nil { 1164 exitStatus = n 1165 } else { 1166 return 0, false, fmt.Errorf("failed to get container state: couldn't parse integer") 1167 } 1168 1169 return exitStatus, running, nil 1170 } 1171 1172 func buildImageCmd(name, dockerfile string, useCache bool, buildFlags ...string) *exec.Cmd { 1173 args := []string{"-D", "build", "-t", name} 1174 if !useCache { 1175 args = append(args, "--no-cache") 1176 } 1177 args = append(args, buildFlags...) 1178 args = append(args, "-") 1179 buildCmd := exec.Command(dockerBinary, args...) 1180 buildCmd.Stdin = strings.NewReader(dockerfile) 1181 return buildCmd 1182 1183 } 1184 1185 func buildImageWithOut(name, dockerfile string, useCache bool, buildFlags ...string) (string, string, error) { 1186 buildCmd := buildImageCmd(name, dockerfile, useCache, buildFlags...) 1187 out, exitCode, err := runCommandWithOutput(buildCmd) 1188 if err != nil || exitCode != 0 { 1189 return "", out, fmt.Errorf("failed to build the image: %s", out) 1190 } 1191 id, err := getIDByName(name) 1192 if err != nil { 1193 return "", out, err 1194 } 1195 return id, out, nil 1196 } 1197 1198 func buildImageWithStdoutStderr(name, dockerfile string, useCache bool, buildFlags ...string) (string, string, string, error) { 1199 buildCmd := buildImageCmd(name, dockerfile, useCache, buildFlags...) 1200 stdout, stderr, exitCode, err := runCommandWithStdoutStderr(buildCmd) 1201 if err != nil || exitCode != 0 { 1202 return "", stdout, stderr, fmt.Errorf("failed to build the image: %s", stdout) 1203 } 1204 id, err := getIDByName(name) 1205 if err != nil { 1206 return "", stdout, stderr, err 1207 } 1208 return id, stdout, stderr, nil 1209 } 1210 1211 func buildImage(name, dockerfile string, useCache bool, buildFlags ...string) (string, error) { 1212 id, _, err := buildImageWithOut(name, dockerfile, useCache, buildFlags...) 1213 return id, err 1214 } 1215 1216 func buildImageFromContext(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, error) { 1217 args := []string{"build", "-t", name} 1218 if !useCache { 1219 args = append(args, "--no-cache") 1220 } 1221 args = append(args, buildFlags...) 1222 args = append(args, ".") 1223 buildCmd := exec.Command(dockerBinary, args...) 1224 buildCmd.Dir = ctx.Dir 1225 out, exitCode, err := runCommandWithOutput(buildCmd) 1226 if err != nil || exitCode != 0 { 1227 return "", fmt.Errorf("failed to build the image: %s", out) 1228 } 1229 return getIDByName(name) 1230 } 1231 1232 func buildImageFromPath(name, path string, useCache bool, buildFlags ...string) (string, error) { 1233 args := []string{"build", "-t", name} 1234 if !useCache { 1235 args = append(args, "--no-cache") 1236 } 1237 args = append(args, buildFlags...) 1238 args = append(args, path) 1239 buildCmd := exec.Command(dockerBinary, args...) 1240 out, exitCode, err := runCommandWithOutput(buildCmd) 1241 if err != nil || exitCode != 0 { 1242 return "", fmt.Errorf("failed to build the image: %s", out) 1243 } 1244 return getIDByName(name) 1245 } 1246 1247 type gitServer interface { 1248 URL() string 1249 Close() error 1250 } 1251 1252 type localGitServer struct { 1253 *httptest.Server 1254 } 1255 1256 func (r *localGitServer) Close() error { 1257 r.Server.Close() 1258 return nil 1259 } 1260 1261 func (r *localGitServer) URL() string { 1262 return r.Server.URL 1263 } 1264 1265 type fakeGit struct { 1266 root string 1267 server gitServer 1268 RepoURL string 1269 } 1270 1271 func (g *fakeGit) Close() { 1272 g.server.Close() 1273 os.RemoveAll(g.root) 1274 } 1275 1276 func newFakeGit(name string, files map[string]string, enforceLocalServer bool) (*fakeGit, error) { 1277 ctx, err := fakeContextWithFiles(files) 1278 if err != nil { 1279 return nil, err 1280 } 1281 defer ctx.Close() 1282 curdir, err := os.Getwd() 1283 if err != nil { 1284 return nil, err 1285 } 1286 defer os.Chdir(curdir) 1287 1288 if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil { 1289 return nil, fmt.Errorf("error trying to init repo: %s (%s)", err, output) 1290 } 1291 err = os.Chdir(ctx.Dir) 1292 if err != nil { 1293 return nil, err 1294 } 1295 if output, err := exec.Command("git", "config", "user.name", "Fake User").CombinedOutput(); err != nil { 1296 return nil, fmt.Errorf("error trying to set 'user.name': %s (%s)", err, output) 1297 } 1298 if output, err := exec.Command("git", "config", "user.email", "fake.user@example.com").CombinedOutput(); err != nil { 1299 return nil, fmt.Errorf("error trying to set 'user.email': %s (%s)", err, output) 1300 } 1301 if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil { 1302 return nil, fmt.Errorf("error trying to add files to repo: %s (%s)", err, output) 1303 } 1304 if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil { 1305 return nil, fmt.Errorf("error trying to commit to repo: %s (%s)", err, output) 1306 } 1307 1308 root, err := ioutil.TempDir("", "docker-test-git-repo") 1309 if err != nil { 1310 return nil, err 1311 } 1312 repoPath := filepath.Join(root, name+".git") 1313 if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil { 1314 os.RemoveAll(root) 1315 return nil, fmt.Errorf("error trying to clone --bare: %s (%s)", err, output) 1316 } 1317 err = os.Chdir(repoPath) 1318 if err != nil { 1319 os.RemoveAll(root) 1320 return nil, err 1321 } 1322 if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil { 1323 os.RemoveAll(root) 1324 return nil, fmt.Errorf("error trying to git update-server-info: %s (%s)", err, output) 1325 } 1326 err = os.Chdir(curdir) 1327 if err != nil { 1328 os.RemoveAll(root) 1329 return nil, err 1330 } 1331 1332 var server gitServer 1333 if !enforceLocalServer { 1334 // use fakeStorage server, which might be local or remote (at test daemon) 1335 server, err = fakeStorageWithContext(fakeContextFromDir(root)) 1336 if err != nil { 1337 return nil, fmt.Errorf("cannot start fake storage: %v", err) 1338 } 1339 } else { 1340 // always start a local http server on CLI test machin 1341 httpServer := httptest.NewServer(http.FileServer(http.Dir(root))) 1342 server = &localGitServer{httpServer} 1343 } 1344 return &fakeGit{ 1345 root: root, 1346 server: server, 1347 RepoURL: fmt.Sprintf("%s/%s.git", server.URL(), name), 1348 }, nil 1349 } 1350 1351 // Write `content` to the file at path `dst`, creating it if necessary, 1352 // as well as any missing directories. 1353 // The file is truncated if it already exists. 1354 // Call c.Fatal() at the first error. 1355 func writeFile(dst, content string, c *check.C) { 1356 // Create subdirectories if necessary 1357 if err := os.MkdirAll(path.Dir(dst), 0700); err != nil { 1358 c.Fatal(err) 1359 } 1360 f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) 1361 if err != nil { 1362 c.Fatal(err) 1363 } 1364 defer f.Close() 1365 // Write content (truncate if it exists) 1366 if _, err := io.Copy(f, strings.NewReader(content)); err != nil { 1367 c.Fatal(err) 1368 } 1369 } 1370 1371 // Return the contents of file at path `src`. 1372 // Call c.Fatal() at the first error (including if the file doesn't exist) 1373 func readFile(src string, c *check.C) (content string) { 1374 data, err := ioutil.ReadFile(src) 1375 if err != nil { 1376 c.Fatal(err) 1377 } 1378 1379 return string(data) 1380 } 1381 1382 func containerStorageFile(containerID, basename string) string { 1383 return filepath.Join(containerStoragePath, containerID, basename) 1384 } 1385 1386 // docker commands that use this function must be run with the '-d' switch. 1387 func runCommandAndReadContainerFile(filename string, cmd *exec.Cmd) ([]byte, error) { 1388 out, _, err := runCommandWithOutput(cmd) 1389 if err != nil { 1390 return nil, fmt.Errorf("%v: %q", err, out) 1391 } 1392 1393 contID := strings.TrimSpace(out) 1394 1395 if err := waitRun(contID); err != nil { 1396 return nil, fmt.Errorf("%v: %q", contID, err) 1397 } 1398 1399 return readContainerFile(contID, filename) 1400 } 1401 1402 func readContainerFile(containerID, filename string) ([]byte, error) { 1403 f, err := os.Open(containerStorageFile(containerID, filename)) 1404 if err != nil { 1405 return nil, err 1406 } 1407 defer f.Close() 1408 1409 content, err := ioutil.ReadAll(f) 1410 if err != nil { 1411 return nil, err 1412 } 1413 1414 return content, nil 1415 } 1416 1417 func readContainerFileWithExec(containerID, filename string) ([]byte, error) { 1418 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "exec", containerID, "cat", filename)) 1419 return []byte(out), err 1420 } 1421 1422 // daemonTime provides the current time on the daemon host 1423 func daemonTime(c *check.C) time.Time { 1424 if isLocalDaemon { 1425 return time.Now() 1426 } 1427 1428 status, body, err := sockRequest("GET", "/info", nil) 1429 c.Assert(status, check.Equals, http.StatusOK) 1430 c.Assert(err, check.IsNil) 1431 1432 type infoJSON struct { 1433 SystemTime string 1434 } 1435 var info infoJSON 1436 if err = json.Unmarshal(body, &info); err != nil { 1437 c.Fatalf("unable to unmarshal /info response: %v", err) 1438 } 1439 1440 dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) 1441 if err != nil { 1442 c.Fatal(err) 1443 } 1444 return dt 1445 } 1446 1447 func setupRegistry(c *check.C) *testRegistryV2 { 1448 testRequires(c, RegistryHosting) 1449 reg, err := newTestRegistryV2(c) 1450 if err != nil { 1451 c.Fatal(err) 1452 } 1453 1454 // Wait for registry to be ready to serve requests. 1455 for i := 0; i != 5; i++ { 1456 if err = reg.Ping(); err == nil { 1457 break 1458 } 1459 time.Sleep(100 * time.Millisecond) 1460 } 1461 1462 if err != nil { 1463 c.Fatal("Timeout waiting for test registry to become available") 1464 } 1465 return reg 1466 } 1467 1468 func setupNotary(c *check.C) *testNotary { 1469 testRequires(c, NotaryHosting) 1470 ts, err := newTestNotary(c) 1471 if err != nil { 1472 c.Fatal(err) 1473 } 1474 1475 return ts 1476 } 1477 1478 // appendBaseEnv appends the minimum set of environment variables to exec the 1479 // docker cli binary for testing with correct configuration to the given env 1480 // list. 1481 func appendBaseEnv(env []string) []string { 1482 preserveList := []string{ 1483 // preserve remote test host 1484 "DOCKER_HOST", 1485 1486 // windows: requires preserving SystemRoot, otherwise dial tcp fails 1487 // with "GetAddrInfoW: A non-recoverable error occurred during a database lookup." 1488 "SystemRoot", 1489 } 1490 1491 for _, key := range preserveList { 1492 if val := os.Getenv(key); val != "" { 1493 env = append(env, fmt.Sprintf("%s=%s", key, val)) 1494 } 1495 } 1496 return env 1497 } 1498 1499 func createTmpFile(c *check.C, content string) string { 1500 f, err := ioutil.TempFile("", "testfile") 1501 c.Assert(err, check.IsNil) 1502 1503 filename := f.Name() 1504 1505 err = ioutil.WriteFile(filename, []byte(content), 0644) 1506 c.Assert(err, check.IsNil) 1507 1508 return filename 1509 } 1510 1511 func buildImageWithOutInDamon(socket string, name, dockerfile string, useCache bool) (string, error) { 1512 args := []string{"--host", socket} 1513 buildCmd := buildImageCmdArgs(args, name, dockerfile, useCache) 1514 out, exitCode, err := runCommandWithOutput(buildCmd) 1515 if err != nil || exitCode != 0 { 1516 return out, fmt.Errorf("failed to build the image: %s, error: %v", out, err) 1517 } 1518 return out, nil 1519 } 1520 1521 func buildImageCmdArgs(args []string, name, dockerfile string, useCache bool) *exec.Cmd { 1522 args = append(args, []string{"-D", "build", "-t", name}...) 1523 if !useCache { 1524 args = append(args, "--no-cache") 1525 } 1526 args = append(args, "-") 1527 buildCmd := exec.Command(dockerBinary, args...) 1528 buildCmd.Stdin = strings.NewReader(dockerfile) 1529 return buildCmd 1530 1531 } 1532 1533 func waitForContainer(contID string, args ...string) error { 1534 args = append([]string{"run", "--name", contID}, args...) 1535 cmd := exec.Command(dockerBinary, args...) 1536 if _, err := runCommand(cmd); err != nil { 1537 return err 1538 } 1539 1540 if err := waitRun(contID); err != nil { 1541 return err 1542 } 1543 1544 return nil 1545 } 1546 1547 // waitRun will wait for the specified container to be running, maximum 5 seconds. 1548 func waitRun(contID string) error { 1549 return waitInspect(contID, "{{.State.Running}}", "true", 5*time.Second) 1550 } 1551 1552 // waitExited will wait for the specified container to state exit, subject 1553 // to a maximum time limit in seconds supplied by the caller 1554 func waitExited(contID string, duration time.Duration) error { 1555 return waitInspect(contID, "{{.State.Status}}", "exited", duration) 1556 } 1557 1558 // waitInspect will wait for the specified container to have the specified string 1559 // in the inspect output. It will wait until the specified timeout (in seconds) 1560 // is reached. 1561 func waitInspect(name, expr, expected string, timeout time.Duration) error { 1562 after := time.After(timeout) 1563 1564 for { 1565 cmd := exec.Command(dockerBinary, "inspect", "-f", expr, name) 1566 out, _, err := runCommandWithOutput(cmd) 1567 if err != nil { 1568 if !strings.Contains(out, "No such") { 1569 return fmt.Errorf("error executing docker inspect: %v\n%s", err, out) 1570 } 1571 select { 1572 case <-after: 1573 return err 1574 default: 1575 time.Sleep(10 * time.Millisecond) 1576 continue 1577 } 1578 } 1579 1580 out = strings.TrimSpace(out) 1581 if out == expected { 1582 break 1583 } 1584 1585 select { 1586 case <-after: 1587 return fmt.Errorf("condition \"%q == %q\" not true in time", out, expected) 1588 default: 1589 } 1590 1591 time.Sleep(100 * time.Millisecond) 1592 } 1593 return nil 1594 } 1595 1596 func getInspectBody(c *check.C, version, id string) []byte { 1597 endpoint := fmt.Sprintf("/%s/containers/%s/json", version, id) 1598 status, body, err := sockRequest("GET", endpoint, nil) 1599 c.Assert(err, check.IsNil) 1600 c.Assert(status, check.Equals, http.StatusOK) 1601 return body 1602 }