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