github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/integration-cli/docker_utils_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net" 11 "net/http" 12 "net/http/httptest" 13 "net/url" 14 "os" 15 "os/exec" 16 "path" 17 "path/filepath" 18 "strconv" 19 "strings" 20 "time" 21 22 "github.com/docker/docker/api/types" 23 volumetypes "github.com/docker/docker/api/types/volume" 24 "github.com/docker/docker/integration-cli/checker" 25 "github.com/docker/docker/integration-cli/daemon" 26 "github.com/docker/docker/integration-cli/registry" 27 "github.com/docker/docker/integration-cli/request" 28 "github.com/docker/docker/opts" 29 "github.com/docker/docker/pkg/stringutils" 30 icmd "github.com/docker/docker/pkg/testutil/cmd" 31 "github.com/go-check/check" 32 ) 33 34 func daemonHost() string { 35 daemonURLStr := "unix://" + opts.DefaultUnixSocket 36 if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" { 37 daemonURLStr = daemonHostVar 38 } 39 return daemonURLStr 40 } 41 42 // FIXME(vdemeester) move this away are remove ignoreNoSuchContainer bool 43 func deleteContainer(ignoreNoSuchContainer bool, container ...string) error { 44 result := icmd.RunCommand(dockerBinary, append([]string{"rm", "-fv"}, container...)...) 45 if ignoreNoSuchContainer && result.Error != nil { 46 // If the error is "No such container: ..." this means the container doesn't exists anymore, 47 // we can safely ignore that one. 48 if strings.Contains(result.Stderr(), "No such container") { 49 return nil 50 } 51 } 52 return result.Compare(icmd.Success) 53 } 54 55 func getAllContainers() (string, error) { 56 getContainersCmd := exec.Command(dockerBinary, "ps", "-q", "-a") 57 out, exitCode, err := runCommandWithOutput(getContainersCmd) 58 if exitCode != 0 && err == nil { 59 err = fmt.Errorf("failed to get a list of containers: %v\n", out) 60 } 61 62 return out, err 63 } 64 65 func deleteAllContainers(c *check.C) { 66 containers, err := getAllContainers() 67 c.Assert(err, checker.IsNil, check.Commentf("containers: %v", containers)) 68 69 if containers != "" { 70 err = deleteContainer(true, strings.Split(strings.TrimSpace(containers), "\n")...) 71 c.Assert(err, checker.IsNil) 72 } 73 } 74 75 func deleteAllNetworks(c *check.C) { 76 networks, err := getAllNetworks() 77 c.Assert(err, check.IsNil) 78 var errs []string 79 for _, n := range networks { 80 if n.Name == "bridge" || n.Name == "none" || n.Name == "host" { 81 continue 82 } 83 if testEnv.DaemonPlatform() == "windows" && strings.ToLower(n.Name) == "nat" { 84 // nat is a pre-defined network on Windows and cannot be removed 85 continue 86 } 87 status, b, err := request.SockRequest("DELETE", "/networks/"+n.Name, nil, daemonHost()) 88 if err != nil { 89 errs = append(errs, err.Error()) 90 continue 91 } 92 if status != http.StatusNoContent { 93 errs = append(errs, fmt.Sprintf("error deleting network %s: %s", n.Name, string(b))) 94 } 95 } 96 c.Assert(errs, checker.HasLen, 0, check.Commentf(strings.Join(errs, "\n"))) 97 } 98 99 func getAllNetworks() ([]types.NetworkResource, error) { 100 var networks []types.NetworkResource 101 _, b, err := request.SockRequest("GET", "/networks", nil, daemonHost()) 102 if err != nil { 103 return nil, err 104 } 105 if err := json.Unmarshal(b, &networks); err != nil { 106 return nil, err 107 } 108 return networks, nil 109 } 110 111 func deleteAllPlugins(c *check.C) { 112 plugins, err := getAllPlugins() 113 c.Assert(err, checker.IsNil) 114 var errs []string 115 for _, p := range plugins { 116 pluginName := p.Name 117 status, b, err := request.SockRequest("DELETE", "/plugins/"+pluginName+"?force=1", nil, daemonHost()) 118 if err != nil { 119 errs = append(errs, err.Error()) 120 continue 121 } 122 if status != http.StatusOK { 123 errs = append(errs, fmt.Sprintf("error deleting plugin %s: %s", p.Name, string(b))) 124 } 125 } 126 c.Assert(errs, checker.HasLen, 0, check.Commentf(strings.Join(errs, "\n"))) 127 } 128 129 func getAllPlugins() (types.PluginsListResponse, error) { 130 var plugins types.PluginsListResponse 131 _, b, err := request.SockRequest("GET", "/plugins", nil, daemonHost()) 132 if err != nil { 133 return nil, err 134 } 135 if err := json.Unmarshal(b, &plugins); err != nil { 136 return nil, err 137 } 138 return plugins, nil 139 } 140 141 func deleteAllVolumes(c *check.C) { 142 volumes, err := getAllVolumes() 143 c.Assert(err, checker.IsNil) 144 var errs []string 145 for _, v := range volumes { 146 status, b, err := request.SockRequest("DELETE", "/volumes/"+v.Name, nil, daemonHost()) 147 if err != nil { 148 errs = append(errs, err.Error()) 149 continue 150 } 151 if status != http.StatusNoContent { 152 errs = append(errs, fmt.Sprintf("error deleting volume %s: %s", v.Name, string(b))) 153 } 154 } 155 c.Assert(errs, checker.HasLen, 0, check.Commentf(strings.Join(errs, "\n"))) 156 } 157 158 func getAllVolumes() ([]*types.Volume, error) { 159 var volumes volumetypes.VolumesListOKBody 160 _, b, err := request.SockRequest("GET", "/volumes", nil, daemonHost()) 161 if err != nil { 162 return nil, err 163 } 164 if err := json.Unmarshal(b, &volumes); err != nil { 165 return nil, err 166 } 167 return volumes.Volumes, nil 168 } 169 170 func deleteAllImages(c *check.C) { 171 cmd := exec.Command(dockerBinary, "images", "--digests") 172 cmd.Env = appendBaseEnv(true) 173 out, err := cmd.CombinedOutput() 174 c.Assert(err, checker.IsNil) 175 lines := strings.Split(string(out), "\n")[1:] 176 imgMap := map[string]struct{}{} 177 for _, l := range lines { 178 if l == "" { 179 continue 180 } 181 fields := strings.Fields(l) 182 imgTag := fields[0] + ":" + fields[1] 183 if _, ok := protectedImages[imgTag]; !ok { 184 if fields[0] == "<none>" || fields[1] == "<none>" { 185 if fields[2] != "<none>" { 186 imgMap[fields[0]+"@"+fields[2]] = struct{}{} 187 } else { 188 imgMap[fields[3]] = struct{}{} 189 } 190 // continue 191 } else { 192 imgMap[imgTag] = struct{}{} 193 } 194 } 195 } 196 if len(imgMap) != 0 { 197 imgs := make([]string, 0, len(imgMap)) 198 for k := range imgMap { 199 imgs = append(imgs, k) 200 } 201 dockerCmd(c, append([]string{"rmi", "-f"}, imgs...)...) 202 } 203 } 204 205 func getPausedContainers() ([]string, error) { 206 getPausedContainersCmd := exec.Command(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") 207 out, exitCode, err := runCommandWithOutput(getPausedContainersCmd) 208 if exitCode != 0 && err == nil { 209 err = fmt.Errorf("failed to get a list of paused containers: %v\n", out) 210 } 211 if err != nil { 212 return nil, err 213 } 214 215 return strings.Fields(out), nil 216 } 217 218 func unpauseContainer(c *check.C, container string) { 219 dockerCmd(c, "unpause", container) 220 } 221 222 func unpauseAllContainers(c *check.C) { 223 containers, err := getPausedContainers() 224 c.Assert(err, checker.IsNil, check.Commentf("containers: %v", containers)) 225 for _, value := range containers { 226 unpauseContainer(c, value) 227 } 228 } 229 230 func deleteImages(images ...string) error { 231 args := []string{dockerBinary, "rmi", "-f"} 232 return icmd.RunCmd(icmd.Cmd{Command: append(args, images...)}).Error 233 } 234 235 func dockerCmdWithError(args ...string) (string, int, error) { 236 if err := validateArgs(args...); err != nil { 237 return "", 0, err 238 } 239 result := icmd.RunCommand(dockerBinary, args...) 240 if result.Error != nil { 241 return result.Combined(), result.ExitCode, result.Compare(icmd.Success) 242 } 243 return result.Combined(), result.ExitCode, result.Error 244 } 245 246 func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) { 247 if err := validateArgs(args...); err != nil { 248 c.Fatalf(err.Error()) 249 } 250 251 result := icmd.RunCommand(dockerBinary, args...) 252 c.Assert(result, icmd.Matches, icmd.Success) 253 return result.Stdout(), result.Stderr(), result.ExitCode 254 } 255 256 func dockerCmd(c *check.C, args ...string) (string, int) { 257 if err := validateArgs(args...); err != nil { 258 c.Fatalf(err.Error()) 259 } 260 result := icmd.RunCommand(dockerBinary, args...) 261 c.Assert(result, icmd.Matches, icmd.Success) 262 return result.Combined(), result.ExitCode 263 } 264 265 func dockerCmdWithResult(args ...string) *icmd.Result { 266 return icmd.RunCommand(dockerBinary, args...) 267 } 268 269 func binaryWithArgs(args ...string) []string { 270 return append([]string{dockerBinary}, args...) 271 } 272 273 // execute a docker command with a timeout 274 func dockerCmdWithTimeout(timeout time.Duration, args ...string) *icmd.Result { 275 if err := validateArgs(args...); err != nil { 276 return &icmd.Result{Error: err} 277 } 278 return icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args...), Timeout: timeout}) 279 } 280 281 // execute a docker command in a directory 282 func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error) { 283 if err := validateArgs(args...); err != nil { 284 c.Fatalf(err.Error()) 285 } 286 result := icmd.RunCmd(icmd.Cmd{Command: binaryWithArgs(args...), Dir: path}) 287 return result.Combined(), result.ExitCode, result.Error 288 } 289 290 // validateArgs is a checker to ensure tests are not running commands which are 291 // not supported on platforms. Specifically on Windows this is 'busybox top'. 292 func validateArgs(args ...string) error { 293 if testEnv.DaemonPlatform() != "windows" { 294 return nil 295 } 296 foundBusybox := -1 297 for key, value := range args { 298 if strings.ToLower(value) == "busybox" { 299 foundBusybox = key 300 } 301 if (foundBusybox != -1) && (key == foundBusybox+1) && (strings.ToLower(value) == "top") { 302 return errors.New("cannot use 'busybox top' in tests on Windows. Use runSleepingContainer()") 303 } 304 } 305 return nil 306 } 307 308 func findContainerIP(c *check.C, id string, network string) string { 309 out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", network), id) 310 return strings.Trim(out, " \r\n'") 311 } 312 313 func getContainerCount() (int, error) { 314 const containers = "Containers:" 315 316 cmd := exec.Command(dockerBinary, "info") 317 out, _, err := runCommandWithOutput(cmd) 318 if err != nil { 319 return 0, err 320 } 321 322 lines := strings.Split(out, "\n") 323 for _, line := range lines { 324 if strings.Contains(line, containers) { 325 output := strings.TrimSpace(line) 326 output = strings.TrimLeft(output, containers) 327 output = strings.Trim(output, " ") 328 containerCount, err := strconv.Atoi(output) 329 if err != nil { 330 return 0, err 331 } 332 return containerCount, nil 333 } 334 } 335 return 0, fmt.Errorf("couldn't find the Container count in the output") 336 } 337 338 // FakeContext creates directories that can be used as a build context 339 type FakeContext struct { 340 Dir string 341 } 342 343 // Add a file at a path, creating directories where necessary 344 func (f *FakeContext) Add(file, content string) error { 345 return f.addFile(file, []byte(content)) 346 } 347 348 func (f *FakeContext) addFile(file string, content []byte) error { 349 fp := filepath.Join(f.Dir, filepath.FromSlash(file)) 350 dirpath := filepath.Dir(fp) 351 if dirpath != "." { 352 if err := os.MkdirAll(dirpath, 0755); err != nil { 353 return err 354 } 355 } 356 return ioutil.WriteFile(fp, content, 0644) 357 358 } 359 360 // Delete a file at a path 361 func (f *FakeContext) Delete(file string) error { 362 fp := filepath.Join(f.Dir, filepath.FromSlash(file)) 363 return os.RemoveAll(fp) 364 } 365 366 // Close deletes the context 367 func (f *FakeContext) Close() error { 368 return os.RemoveAll(f.Dir) 369 } 370 371 func fakeContextFromNewTempDir() (*FakeContext, error) { 372 tmp, err := ioutil.TempDir("", "fake-context") 373 if err != nil { 374 return nil, err 375 } 376 if err := os.Chmod(tmp, 0755); err != nil { 377 return nil, err 378 } 379 return fakeContextFromDir(tmp), nil 380 } 381 382 func fakeContextFromDir(dir string) *FakeContext { 383 return &FakeContext{dir} 384 } 385 386 func fakeContextWithFiles(files map[string]string) (*FakeContext, error) { 387 ctx, err := fakeContextFromNewTempDir() 388 if err != nil { 389 return nil, err 390 } 391 for file, content := range files { 392 if err := ctx.Add(file, content); err != nil { 393 ctx.Close() 394 return nil, err 395 } 396 } 397 return ctx, nil 398 } 399 400 func fakeContextAddDockerfile(ctx *FakeContext, dockerfile string) error { 401 if err := ctx.Add("Dockerfile", dockerfile); err != nil { 402 ctx.Close() 403 return err 404 } 405 return nil 406 } 407 408 func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) { 409 ctx, err := fakeContextWithFiles(files) 410 if err != nil { 411 return nil, err 412 } 413 if err := fakeContextAddDockerfile(ctx, dockerfile); err != nil { 414 return nil, err 415 } 416 return ctx, nil 417 } 418 419 // FakeStorage is a static file server. It might be running locally or remotely 420 // on test host. 421 type FakeStorage interface { 422 Close() error 423 URL() string 424 CtxDir() string 425 } 426 427 func fakeBinaryStorage(archives map[string]*bytes.Buffer) (FakeStorage, error) { 428 ctx, err := fakeContextFromNewTempDir() 429 if err != nil { 430 return nil, err 431 } 432 for name, content := range archives { 433 if err := ctx.addFile(name, content.Bytes()); err != nil { 434 return nil, err 435 } 436 } 437 return fakeStorageWithContext(ctx) 438 } 439 440 // fakeStorage returns either a local or remote (at daemon machine) file server 441 func fakeStorage(files map[string]string) (FakeStorage, error) { 442 ctx, err := fakeContextWithFiles(files) 443 if err != nil { 444 return nil, err 445 } 446 return fakeStorageWithContext(ctx) 447 } 448 449 // fakeStorageWithContext returns either a local or remote (at daemon machine) file server 450 func fakeStorageWithContext(ctx *FakeContext) (FakeStorage, error) { 451 if testEnv.LocalDaemon() { 452 return newLocalFakeStorage(ctx) 453 } 454 return newRemoteFileServer(ctx) 455 } 456 457 // localFileStorage is a file storage on the running machine 458 type localFileStorage struct { 459 *FakeContext 460 *httptest.Server 461 } 462 463 func (s *localFileStorage) URL() string { 464 return s.Server.URL 465 } 466 467 func (s *localFileStorage) CtxDir() string { 468 return s.FakeContext.Dir 469 } 470 471 func (s *localFileStorage) Close() error { 472 defer s.Server.Close() 473 return s.FakeContext.Close() 474 } 475 476 func newLocalFakeStorage(ctx *FakeContext) (*localFileStorage, error) { 477 handler := http.FileServer(http.Dir(ctx.Dir)) 478 server := httptest.NewServer(handler) 479 return &localFileStorage{ 480 FakeContext: ctx, 481 Server: server, 482 }, nil 483 } 484 485 // remoteFileServer is a containerized static file server started on the remote 486 // testing machine to be used in URL-accepting docker build functionality. 487 type remoteFileServer struct { 488 host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712 489 container string 490 image string 491 ctx *FakeContext 492 } 493 494 func (f *remoteFileServer) URL() string { 495 u := url.URL{ 496 Scheme: "http", 497 Host: f.host} 498 return u.String() 499 } 500 501 func (f *remoteFileServer) CtxDir() string { 502 return f.ctx.Dir 503 } 504 505 func (f *remoteFileServer) Close() error { 506 defer func() { 507 if f.ctx != nil { 508 f.ctx.Close() 509 } 510 if f.image != "" { 511 deleteImages(f.image) 512 } 513 }() 514 if f.container == "" { 515 return nil 516 } 517 return deleteContainer(false, f.container) 518 } 519 520 func newRemoteFileServer(ctx *FakeContext) (*remoteFileServer, error) { 521 var ( 522 image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10))) 523 container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10))) 524 ) 525 526 if err := ensureHTTPServerImage(); err != nil { 527 return nil, err 528 } 529 530 // Build the image 531 if err := fakeContextAddDockerfile(ctx, `FROM httpserver 532 COPY . /static`); err != nil { 533 return nil, fmt.Errorf("Cannot add Dockerfile to context: %v", err) 534 } 535 if _, err := buildImageFromContext(image, ctx, false); err != nil { 536 return nil, fmt.Errorf("failed building file storage container image: %v", err) 537 } 538 539 // Start the container 540 runCmd := exec.Command(dockerBinary, "run", "-d", "-P", "--name", container, image) 541 if out, ec, err := runCommandWithOutput(runCmd); err != nil { 542 return nil, fmt.Errorf("failed to start file storage container. ec=%v\nout=%s\nerr=%v", ec, out, err) 543 } 544 545 // Find out the system assigned port 546 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "port", container, "80/tcp")) 547 if err != nil { 548 return nil, fmt.Errorf("failed to find container port: err=%v\nout=%s", err, out) 549 } 550 551 fileserverHostPort := strings.Trim(out, "\n") 552 _, port, err := net.SplitHostPort(fileserverHostPort) 553 if err != nil { 554 return nil, fmt.Errorf("unable to parse file server host:port: %v", err) 555 } 556 557 dockerHostURL, err := url.Parse(daemonHost()) 558 if err != nil { 559 return nil, fmt.Errorf("unable to parse daemon host URL: %v", err) 560 } 561 562 host, _, err := net.SplitHostPort(dockerHostURL.Host) 563 if err != nil { 564 return nil, fmt.Errorf("unable to parse docker daemon host:port: %v", err) 565 } 566 567 return &remoteFileServer{ 568 container: container, 569 image: image, 570 host: fmt.Sprintf("%s:%s", host, port), 571 ctx: ctx}, nil 572 } 573 574 func inspectFieldAndUnmarshall(c *check.C, name, field string, output interface{}) { 575 str := inspectFieldJSON(c, name, field) 576 err := json.Unmarshal([]byte(str), output) 577 if c != nil { 578 c.Assert(err, check.IsNil, check.Commentf("failed to unmarshal: %v", err)) 579 } 580 } 581 582 func inspectFilter(name, filter string) (string, error) { 583 format := fmt.Sprintf("{{%s}}", filter) 584 result := icmd.RunCommand(dockerBinary, "inspect", "-f", format, name) 585 if result.Error != nil || result.ExitCode != 0 { 586 return "", fmt.Errorf("failed to inspect %s: %s", name, result.Combined()) 587 } 588 return strings.TrimSpace(result.Combined()), nil 589 } 590 591 func inspectFieldWithError(name, field string) (string, error) { 592 return inspectFilter(name, fmt.Sprintf(".%s", field)) 593 } 594 595 func inspectField(c *check.C, name, field string) string { 596 out, err := inspectFilter(name, fmt.Sprintf(".%s", field)) 597 if c != nil { 598 c.Assert(err, check.IsNil) 599 } 600 return out 601 } 602 603 func inspectFieldJSON(c *check.C, name, field string) string { 604 out, err := inspectFilter(name, fmt.Sprintf("json .%s", field)) 605 if c != nil { 606 c.Assert(err, check.IsNil) 607 } 608 return out 609 } 610 611 func inspectFieldMap(c *check.C, name, path, field string) string { 612 out, err := inspectFilter(name, fmt.Sprintf("index .%s %q", path, field)) 613 if c != nil { 614 c.Assert(err, check.IsNil) 615 } 616 return out 617 } 618 619 func inspectMountSourceField(name, destination string) (string, error) { 620 m, err := inspectMountPoint(name, destination) 621 if err != nil { 622 return "", err 623 } 624 return m.Source, nil 625 } 626 627 func inspectMountPoint(name, destination string) (types.MountPoint, error) { 628 out, err := inspectFilter(name, "json .Mounts") 629 if err != nil { 630 return types.MountPoint{}, err 631 } 632 633 return inspectMountPointJSON(out, destination) 634 } 635 636 var errMountNotFound = errors.New("mount point not found") 637 638 func inspectMountPointJSON(j, destination string) (types.MountPoint, error) { 639 var mp []types.MountPoint 640 if err := json.Unmarshal([]byte(j), &mp); err != nil { 641 return types.MountPoint{}, err 642 } 643 644 var m *types.MountPoint 645 for _, c := range mp { 646 if c.Destination == destination { 647 m = &c 648 break 649 } 650 } 651 652 if m == nil { 653 return types.MountPoint{}, errMountNotFound 654 } 655 656 return *m, nil 657 } 658 659 func inspectImage(name, filter string) (string, error) { 660 args := []string{"inspect", "--type", "image"} 661 if filter != "" { 662 format := fmt.Sprintf("{{%s}}", filter) 663 args = append(args, "-f", format) 664 } 665 args = append(args, name) 666 inspectCmd := exec.Command(dockerBinary, args...) 667 out, exitCode, err := runCommandWithOutput(inspectCmd) 668 if err != nil || exitCode != 0 { 669 return "", fmt.Errorf("failed to inspect %s: %s", name, out) 670 } 671 return strings.TrimSpace(out), nil 672 } 673 674 func getIDByName(name string) (string, error) { 675 return inspectFieldWithError(name, "Id") 676 } 677 678 // Deprecated 679 func buildImageCmd(name, dockerfile string, useCache bool, buildFlags ...string) *exec.Cmd { 680 return daemon.BuildImageCmdWithHost(dockerBinary, name, dockerfile, "", useCache, buildFlags...) 681 } 682 683 // Deprecated 684 func buildImageWithOut(name, dockerfile string, useCache bool, buildFlags ...string) (string, string, error) { 685 buildCmd := buildImageCmd(name, dockerfile, useCache, buildFlags...) 686 out, exitCode, err := runCommandWithOutput(buildCmd) 687 if err != nil || exitCode != 0 { 688 return "", out, fmt.Errorf("failed to build the image: %s", out) 689 } 690 id, err := getIDByName(name) 691 if err != nil { 692 return "", out, err 693 } 694 return id, out, nil 695 } 696 697 // Deprecated 698 func buildImageWithStdoutStderr(name, dockerfile string, useCache bool, buildFlags ...string) (string, string, string, error) { 699 buildCmd := buildImageCmd(name, dockerfile, useCache, buildFlags...) 700 result := icmd.RunCmd(transformCmd(buildCmd)) 701 err := result.Error 702 exitCode := result.ExitCode 703 if err != nil || exitCode != 0 { 704 return "", result.Stdout(), result.Stderr(), fmt.Errorf("failed to build the image: %s", result.Combined()) 705 } 706 id, err := getIDByName(name) 707 if err != nil { 708 return "", result.Stdout(), result.Stderr(), err 709 } 710 return id, result.Stdout(), result.Stderr(), nil 711 } 712 713 func buildImageSuccessfully(c *check.C, name string, cmdOperators ...func(*icmd.Cmd) func()) { 714 buildImageNew(name, cmdOperators...).Assert(c, icmd.Success) 715 } 716 717 // FIXME(vdemeester) rename this buildImage once deprecated buildImage is no more 718 func buildImageNew(name string, cmdOperators ...func(*icmd.Cmd) func()) *icmd.Result { 719 cmd := icmd.Command(dockerBinary, "build", "-t", name) 720 for _, op := range cmdOperators { 721 deferFn := op(&cmd) 722 if deferFn != nil { 723 defer deferFn() 724 } 725 } 726 return icmd.RunCmd(cmd) 727 } 728 729 func withBuildContextPath(path string) func(*icmd.Cmd) func() { 730 return func(cmd *icmd.Cmd) func() { 731 cmd.Command = append(cmd.Command, path) 732 return nil 733 } 734 } 735 736 func withBuildContext(c *check.C, contextOperators ...func(*FakeContext) error) func(*icmd.Cmd) func() { 737 ctx, err := fakeContextFromNewTempDir() 738 if err != nil { 739 c.Fatalf("error creating build context : %v", err) 740 } 741 for _, op := range contextOperators { 742 if err := op(ctx); err != nil { 743 c.Fatal(err) 744 } 745 } 746 return func(cmd *icmd.Cmd) func() { 747 cmd.Dir = ctx.Dir 748 cmd.Command = append(cmd.Command, ".") 749 return closeBuildContext(c, ctx) 750 } 751 } 752 753 func withBuildFlags(flags ...string) func(*icmd.Cmd) func() { 754 return func(cmd *icmd.Cmd) func() { 755 cmd.Command = append(cmd.Command, flags...) 756 return nil 757 } 758 } 759 760 func withoutCache(cmd *icmd.Cmd) func() { 761 cmd.Command = append(cmd.Command, "--no-cache") 762 return nil 763 } 764 765 func withFile(name, content string) func(*FakeContext) error { 766 return func(ctx *FakeContext) error { 767 return ctx.Add(name, content) 768 } 769 } 770 771 func closeBuildContext(c *check.C, ctx *FakeContext) func() { 772 return func() { 773 if err := ctx.Close(); err != nil { 774 c.Fatal(err) 775 } 776 } 777 } 778 779 func withDockerfile(dockerfile string) func(*icmd.Cmd) func() { 780 return func(cmd *icmd.Cmd) func() { 781 cmd.Command = append(cmd.Command, "-") 782 cmd.Stdin = strings.NewReader(dockerfile) 783 return nil 784 } 785 } 786 787 func trustedBuild(cmd *icmd.Cmd) func() { 788 trustedCmd(cmd) 789 return nil 790 } 791 792 func withEnvironmentVariales(envs ...string) func(cmd *icmd.Cmd) func() { 793 return func(cmd *icmd.Cmd) func() { 794 cmd.Env = envs 795 return nil 796 } 797 } 798 799 // Deprecated 800 func buildImage(name, dockerfile string, useCache bool, buildFlags ...string) (string, error) { 801 id, _, err := buildImageWithOut(name, dockerfile, useCache, buildFlags...) 802 return id, err 803 } 804 805 // Deprecated 806 func buildImageFromContext(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, error) { 807 id, _, err := buildImageFromContextWithOut(name, ctx, useCache, buildFlags...) 808 if err != nil { 809 return "", err 810 } 811 return id, nil 812 } 813 814 // Deprecated 815 func buildImageFromContextWithOut(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, string, error) { 816 args := []string{"build", "-t", name} 817 if !useCache { 818 args = append(args, "--no-cache") 819 } 820 args = append(args, buildFlags...) 821 args = append(args, ".") 822 result := icmd.RunCmd(icmd.Cmd{ 823 Command: append([]string{dockerBinary}, args...), 824 Dir: ctx.Dir, 825 }) 826 out := result.Combined() 827 if result.Error != nil || result.ExitCode != 0 { 828 return "", "", fmt.Errorf("failed to build the image: %s", out) 829 } 830 id, err := getIDByName(name) 831 if err != nil { 832 return "", "", err 833 } 834 return id, out, nil 835 } 836 837 // Deprecated 838 func buildImageFromContextWithStdoutStderr(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, string, string, error) { 839 args := []string{"build", "-t", name} 840 if !useCache { 841 args = append(args, "--no-cache") 842 } 843 args = append(args, buildFlags...) 844 args = append(args, ".") 845 846 result := icmd.RunCmd(icmd.Cmd{ 847 Command: append([]string{dockerBinary}, args...), 848 Dir: ctx.Dir, 849 }) 850 exitCode := result.ExitCode 851 err := result.Error 852 if err != nil || exitCode != 0 { 853 return "", result.Stdout(), result.Stderr(), fmt.Errorf("failed to build the image: %s", result.Combined()) 854 } 855 id, err := getIDByName(name) 856 if err != nil { 857 return "", result.Stdout(), result.Stderr(), err 858 } 859 return id, result.Stdout(), result.Stderr(), nil 860 } 861 862 // Deprecated 863 func buildImageFromGitWithStdoutStderr(name string, ctx *fakeGit, useCache bool, buildFlags ...string) (string, string, string, error) { 864 args := []string{"build", "-t", name} 865 if !useCache { 866 args = append(args, "--no-cache") 867 } 868 args = append(args, buildFlags...) 869 args = append(args, ctx.RepoURL) 870 result := icmd.RunCmd(icmd.Cmd{ 871 Command: append([]string{dockerBinary}, args...), 872 }) 873 exitCode := result.ExitCode 874 err := result.Error 875 if err != nil || exitCode != 0 { 876 return "", result.Stdout(), result.Stderr(), fmt.Errorf("failed to build the image: %s", result.Combined()) 877 } 878 id, err := getIDByName(name) 879 if err != nil { 880 return "", result.Stdout(), result.Stderr(), err 881 } 882 return id, result.Stdout(), result.Stderr(), nil 883 } 884 885 // Deprecated 886 func buildImageFromPath(name, path string, useCache bool, buildFlags ...string) (string, error) { 887 args := []string{"build", "-t", name} 888 if !useCache { 889 args = append(args, "--no-cache") 890 } 891 args = append(args, buildFlags...) 892 args = append(args, path) 893 buildCmd := exec.Command(dockerBinary, args...) 894 out, exitCode, err := runCommandWithOutput(buildCmd) 895 if err != nil || exitCode != 0 { 896 return "", fmt.Errorf("failed to build the image: %s", out) 897 } 898 return getIDByName(name) 899 } 900 901 type gitServer interface { 902 URL() string 903 Close() error 904 } 905 906 type localGitServer struct { 907 *httptest.Server 908 } 909 910 func (r *localGitServer) Close() error { 911 r.Server.Close() 912 return nil 913 } 914 915 func (r *localGitServer) URL() string { 916 return r.Server.URL 917 } 918 919 type fakeGit struct { 920 root string 921 server gitServer 922 RepoURL string 923 } 924 925 func (g *fakeGit) Close() { 926 g.server.Close() 927 os.RemoveAll(g.root) 928 } 929 930 func newFakeGit(name string, files map[string]string, enforceLocalServer bool) (*fakeGit, error) { 931 ctx, err := fakeContextWithFiles(files) 932 if err != nil { 933 return nil, err 934 } 935 defer ctx.Close() 936 curdir, err := os.Getwd() 937 if err != nil { 938 return nil, err 939 } 940 defer os.Chdir(curdir) 941 942 if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil { 943 return nil, fmt.Errorf("error trying to init repo: %s (%s)", err, output) 944 } 945 err = os.Chdir(ctx.Dir) 946 if err != nil { 947 return nil, err 948 } 949 if output, err := exec.Command("git", "config", "user.name", "Fake User").CombinedOutput(); err != nil { 950 return nil, fmt.Errorf("error trying to set 'user.name': %s (%s)", err, output) 951 } 952 if output, err := exec.Command("git", "config", "user.email", "fake.user@example.com").CombinedOutput(); err != nil { 953 return nil, fmt.Errorf("error trying to set 'user.email': %s (%s)", err, output) 954 } 955 if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil { 956 return nil, fmt.Errorf("error trying to add files to repo: %s (%s)", err, output) 957 } 958 if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil { 959 return nil, fmt.Errorf("error trying to commit to repo: %s (%s)", err, output) 960 } 961 962 root, err := ioutil.TempDir("", "docker-test-git-repo") 963 if err != nil { 964 return nil, err 965 } 966 repoPath := filepath.Join(root, name+".git") 967 if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil { 968 os.RemoveAll(root) 969 return nil, fmt.Errorf("error trying to clone --bare: %s (%s)", err, output) 970 } 971 err = os.Chdir(repoPath) 972 if err != nil { 973 os.RemoveAll(root) 974 return nil, err 975 } 976 if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil { 977 os.RemoveAll(root) 978 return nil, fmt.Errorf("error trying to git update-server-info: %s (%s)", err, output) 979 } 980 err = os.Chdir(curdir) 981 if err != nil { 982 os.RemoveAll(root) 983 return nil, err 984 } 985 986 var server gitServer 987 if !enforceLocalServer { 988 // use fakeStorage server, which might be local or remote (at test daemon) 989 server, err = fakeStorageWithContext(fakeContextFromDir(root)) 990 if err != nil { 991 return nil, fmt.Errorf("cannot start fake storage: %v", err) 992 } 993 } else { 994 // always start a local http server on CLI test machine 995 httpServer := httptest.NewServer(http.FileServer(http.Dir(root))) 996 server = &localGitServer{httpServer} 997 } 998 return &fakeGit{ 999 root: root, 1000 server: server, 1001 RepoURL: fmt.Sprintf("%s/%s.git", server.URL(), name), 1002 }, nil 1003 } 1004 1005 // Write `content` to the file at path `dst`, creating it if necessary, 1006 // as well as any missing directories. 1007 // The file is truncated if it already exists. 1008 // Fail the test when error occurs. 1009 func writeFile(dst, content string, c *check.C) { 1010 // Create subdirectories if necessary 1011 c.Assert(os.MkdirAll(path.Dir(dst), 0700), check.IsNil) 1012 f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) 1013 c.Assert(err, check.IsNil) 1014 defer f.Close() 1015 // Write content (truncate if it exists) 1016 _, err = io.Copy(f, strings.NewReader(content)) 1017 c.Assert(err, check.IsNil) 1018 } 1019 1020 // Return the contents of file at path `src`. 1021 // Fail the test when error occurs. 1022 func readFile(src string, c *check.C) (content string) { 1023 data, err := ioutil.ReadFile(src) 1024 c.Assert(err, check.IsNil) 1025 1026 return string(data) 1027 } 1028 1029 func containerStorageFile(containerID, basename string) string { 1030 return filepath.Join(testEnv.ContainerStoragePath(), containerID, basename) 1031 } 1032 1033 // docker commands that use this function must be run with the '-d' switch. 1034 func runCommandAndReadContainerFile(filename string, cmd *exec.Cmd) ([]byte, error) { 1035 out, _, err := runCommandWithOutput(cmd) 1036 if err != nil { 1037 return nil, fmt.Errorf("%v: %q", err, out) 1038 } 1039 1040 contID := strings.TrimSpace(out) 1041 1042 if err := waitRun(contID); err != nil { 1043 return nil, fmt.Errorf("%v: %q", contID, err) 1044 } 1045 1046 return readContainerFile(contID, filename) 1047 } 1048 1049 func readContainerFile(containerID, filename string) ([]byte, error) { 1050 f, err := os.Open(containerStorageFile(containerID, filename)) 1051 if err != nil { 1052 return nil, err 1053 } 1054 defer f.Close() 1055 1056 content, err := ioutil.ReadAll(f) 1057 if err != nil { 1058 return nil, err 1059 } 1060 1061 return content, nil 1062 } 1063 1064 func readContainerFileWithExec(containerID, filename string) ([]byte, error) { 1065 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "exec", containerID, "cat", filename)) 1066 return []byte(out), err 1067 } 1068 1069 // daemonTime provides the current time on the daemon host 1070 func daemonTime(c *check.C) time.Time { 1071 if testEnv.LocalDaemon() { 1072 return time.Now() 1073 } 1074 1075 status, body, err := request.SockRequest("GET", "/info", nil, daemonHost()) 1076 c.Assert(err, check.IsNil) 1077 c.Assert(status, check.Equals, http.StatusOK) 1078 1079 type infoJSON struct { 1080 SystemTime string 1081 } 1082 var info infoJSON 1083 err = json.Unmarshal(body, &info) 1084 c.Assert(err, check.IsNil, check.Commentf("unable to unmarshal GET /info response")) 1085 1086 dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) 1087 c.Assert(err, check.IsNil, check.Commentf("invalid time format in GET /info response")) 1088 return dt 1089 } 1090 1091 // daemonUnixTime returns the current time on the daemon host with nanoseconds precision. 1092 // It return the time formatted how the client sends timestamps to the server. 1093 func daemonUnixTime(c *check.C) string { 1094 return parseEventTime(daemonTime(c)) 1095 } 1096 1097 func parseEventTime(t time.Time) string { 1098 return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())) 1099 } 1100 1101 func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *registry.V2 { 1102 reg, err := registry.NewV2(schema1, auth, tokenURL, privateRegistryURL) 1103 c.Assert(err, check.IsNil) 1104 1105 // Wait for registry to be ready to serve requests. 1106 for i := 0; i != 50; i++ { 1107 if err = reg.Ping(); err == nil { 1108 break 1109 } 1110 time.Sleep(100 * time.Millisecond) 1111 } 1112 1113 c.Assert(err, check.IsNil, check.Commentf("Timeout waiting for test registry to become available: %v", err)) 1114 return reg 1115 } 1116 1117 func setupNotary(c *check.C) *testNotary { 1118 ts, err := newTestNotary(c) 1119 c.Assert(err, check.IsNil) 1120 1121 return ts 1122 } 1123 1124 // appendBaseEnv appends the minimum set of environment variables to exec the 1125 // docker cli binary for testing with correct configuration to the given env 1126 // list. 1127 func appendBaseEnv(isTLS bool, env ...string) []string { 1128 preserveList := []string{ 1129 // preserve remote test host 1130 "DOCKER_HOST", 1131 1132 // windows: requires preserving SystemRoot, otherwise dial tcp fails 1133 // with "GetAddrInfoW: A non-recoverable error occurred during a database lookup." 1134 "SystemRoot", 1135 1136 // testing help text requires the $PATH to dockerd is set 1137 "PATH", 1138 } 1139 if isTLS { 1140 preserveList = append(preserveList, "DOCKER_TLS_VERIFY", "DOCKER_CERT_PATH") 1141 } 1142 1143 for _, key := range preserveList { 1144 if val := os.Getenv(key); val != "" { 1145 env = append(env, fmt.Sprintf("%s=%s", key, val)) 1146 } 1147 } 1148 return env 1149 } 1150 1151 func createTmpFile(c *check.C, content string) string { 1152 f, err := ioutil.TempFile("", "testfile") 1153 c.Assert(err, check.IsNil) 1154 1155 filename := f.Name() 1156 1157 err = ioutil.WriteFile(filename, []byte(content), 0644) 1158 c.Assert(err, check.IsNil) 1159 1160 return filename 1161 } 1162 1163 func waitForContainer(contID string, args ...string) error { 1164 args = append([]string{dockerBinary, "run", "--name", contID}, args...) 1165 result := icmd.RunCmd(icmd.Cmd{Command: args}) 1166 if result.Error != nil { 1167 return result.Error 1168 } 1169 return waitRun(contID) 1170 } 1171 1172 // waitRestart will wait for the specified container to restart once 1173 func waitRestart(contID string, duration time.Duration) error { 1174 return waitInspect(contID, "{{.RestartCount}}", "1", duration) 1175 } 1176 1177 // waitRun will wait for the specified container to be running, maximum 5 seconds. 1178 func waitRun(contID string) error { 1179 return waitInspect(contID, "{{.State.Running}}", "true", 5*time.Second) 1180 } 1181 1182 // waitExited will wait for the specified container to state exit, subject 1183 // to a maximum time limit in seconds supplied by the caller 1184 func waitExited(contID string, duration time.Duration) error { 1185 return waitInspect(contID, "{{.State.Status}}", "exited", duration) 1186 } 1187 1188 // waitInspect will wait for the specified container to have the specified string 1189 // in the inspect output. It will wait until the specified timeout (in seconds) 1190 // is reached. 1191 func waitInspect(name, expr, expected string, timeout time.Duration) error { 1192 return waitInspectWithArgs(name, expr, expected, timeout) 1193 } 1194 1195 func waitInspectWithArgs(name, expr, expected string, timeout time.Duration, arg ...string) error { 1196 return daemon.WaitInspectWithArgs(dockerBinary, name, expr, expected, timeout, arg...) 1197 } 1198 1199 func getInspectBody(c *check.C, version, id string) []byte { 1200 endpoint := fmt.Sprintf("/%s/containers/%s/json", version, id) 1201 status, body, err := request.SockRequest("GET", endpoint, nil, daemonHost()) 1202 c.Assert(err, check.IsNil) 1203 c.Assert(status, check.Equals, http.StatusOK) 1204 return body 1205 } 1206 1207 // Run a long running idle task in a background container using the 1208 // system-specific default image and command. 1209 func runSleepingContainer(c *check.C, extraArgs ...string) (string, int) { 1210 return runSleepingContainerInImage(c, defaultSleepImage, extraArgs...) 1211 } 1212 1213 // Run a long running idle task in a background container using the specified 1214 // image and the system-specific command. 1215 func runSleepingContainerInImage(c *check.C, image string, extraArgs ...string) (string, int) { 1216 args := []string{"run", "-d"} 1217 args = append(args, extraArgs...) 1218 args = append(args, image) 1219 args = append(args, sleepCommandForDaemonPlatform()...) 1220 return dockerCmd(c, args...) 1221 } 1222 1223 // minimalBaseImage returns the name of the minimal base image for the current 1224 // daemon platform. 1225 func minimalBaseImage() string { 1226 return testEnv.MinimalBaseImage() 1227 } 1228 1229 func getGoroutineNumber() (int, error) { 1230 i := struct { 1231 NGoroutines int 1232 }{} 1233 status, b, err := request.SockRequest("GET", "/info", nil, daemonHost()) 1234 if err != nil { 1235 return 0, err 1236 } 1237 if status != http.StatusOK { 1238 return 0, fmt.Errorf("http status code: %d", status) 1239 } 1240 if err := json.Unmarshal(b, &i); err != nil { 1241 return 0, err 1242 } 1243 return i.NGoroutines, nil 1244 } 1245 1246 func waitForGoroutines(expected int) error { 1247 t := time.After(30 * time.Second) 1248 for { 1249 select { 1250 case <-t: 1251 n, err := getGoroutineNumber() 1252 if err != nil { 1253 return err 1254 } 1255 if n > expected { 1256 return fmt.Errorf("leaked goroutines: expected less than or equal to %d, got: %d", expected, n) 1257 } 1258 default: 1259 n, err := getGoroutineNumber() 1260 if err != nil { 1261 return err 1262 } 1263 if n <= expected { 1264 return nil 1265 } 1266 time.Sleep(200 * time.Millisecond) 1267 } 1268 } 1269 } 1270 1271 // getErrorMessage returns the error message from an error API response 1272 func getErrorMessage(c *check.C, body []byte) string { 1273 var resp types.ErrorResponse 1274 c.Assert(json.Unmarshal(body, &resp), check.IsNil) 1275 return strings.TrimSpace(resp.Message) 1276 } 1277 1278 func waitAndAssert(c *check.C, timeout time.Duration, f checkF, checker check.Checker, args ...interface{}) { 1279 after := time.After(timeout) 1280 for { 1281 v, comment := f(c) 1282 assert, _ := checker.Check(append([]interface{}{v}, args...), checker.Info().Params) 1283 select { 1284 case <-after: 1285 assert = true 1286 default: 1287 } 1288 if assert { 1289 if comment != nil { 1290 args = append(args, comment) 1291 } 1292 c.Assert(v, checker, args...) 1293 return 1294 } 1295 time.Sleep(100 * time.Millisecond) 1296 } 1297 } 1298 1299 type checkF func(*check.C) (interface{}, check.CommentInterface) 1300 type reducer func(...interface{}) interface{} 1301 1302 func reducedCheck(r reducer, funcs ...checkF) checkF { 1303 return func(c *check.C) (interface{}, check.CommentInterface) { 1304 var values []interface{} 1305 var comments []string 1306 for _, f := range funcs { 1307 v, comment := f(c) 1308 values = append(values, v) 1309 if comment != nil { 1310 comments = append(comments, comment.CheckCommentString()) 1311 } 1312 } 1313 return r(values...), check.Commentf("%v", strings.Join(comments, ", ")) 1314 } 1315 } 1316 1317 func sumAsIntegers(vals ...interface{}) interface{} { 1318 var s int 1319 for _, v := range vals { 1320 s += v.(int) 1321 } 1322 return s 1323 }