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