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