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