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