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