github.com/torfuzx/docker@v1.8.1/integration-cli/docker_utils.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/http/httputil" 14 "net/url" 15 "os" 16 "os/exec" 17 "path" 18 "path/filepath" 19 "strconv" 20 "strings" 21 "time" 22 23 "github.com/docker/docker/api/types" 24 "github.com/docker/docker/opts" 25 "github.com/docker/docker/pkg/ioutils" 26 "github.com/docker/docker/pkg/stringutils" 27 "github.com/go-check/check" 28 ) 29 30 // Daemon represents a Docker daemon for the testing framework. 31 type Daemon struct { 32 // Defaults to "daemon" 33 // Useful to set to --daemon or -d for checking backwards compatability 34 Command string 35 GlobalFlags []string 36 37 id string 38 c *check.C 39 logFile *os.File 40 folder string 41 stdin io.WriteCloser 42 stdout, stderr io.ReadCloser 43 cmd *exec.Cmd 44 storageDriver string 45 execDriver string 46 wait chan error 47 userlandProxy bool 48 } 49 50 func enableUserlandProxy() bool { 51 if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" { 52 if val, err := strconv.ParseBool(env); err != nil { 53 return val 54 } 55 } 56 return true 57 } 58 59 // NewDaemon returns a Daemon instance to be used for testing. 60 // This will create a directory such as d123456789 in the folder specified by $DEST. 61 // The daemon will not automatically start. 62 func NewDaemon(c *check.C) *Daemon { 63 dest := os.Getenv("DEST") 64 if dest == "" { 65 c.Fatal("Please set the DEST environment variable") 66 } 67 68 id := fmt.Sprintf("d%d", time.Now().UnixNano()%100000000) 69 dir := filepath.Join(dest, id) 70 daemonFolder, err := filepath.Abs(dir) 71 if err != nil { 72 c.Fatalf("Could not make %q an absolute path: %v", dir, err) 73 } 74 75 if err := os.MkdirAll(filepath.Join(daemonFolder, "graph"), 0600); err != nil { 76 c.Fatalf("Could not create %s/graph directory", daemonFolder) 77 } 78 79 userlandProxy := true 80 if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" { 81 if val, err := strconv.ParseBool(env); err != nil { 82 userlandProxy = val 83 } 84 } 85 86 return &Daemon{ 87 Command: "daemon", 88 id: id, 89 c: c, 90 folder: daemonFolder, 91 storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"), 92 execDriver: os.Getenv("DOCKER_EXECDRIVER"), 93 userlandProxy: userlandProxy, 94 } 95 } 96 97 // Start will start the daemon and return once it is ready to receive requests. 98 // You can specify additional daemon flags. 99 func (d *Daemon) Start(arg ...string) error { 100 dockerBinary, err := exec.LookPath(dockerBinary) 101 if err != nil { 102 d.c.Fatalf("[%s] could not find docker binary in $PATH: %v", d.id, err) 103 } 104 105 args := append(d.GlobalFlags, 106 d.Command, 107 "--host", d.sock(), 108 "--graph", fmt.Sprintf("%s/graph", d.folder), 109 "--pidfile", fmt.Sprintf("%s/docker.pid", d.folder), 110 fmt.Sprintf("--userland-proxy=%t", d.userlandProxy), 111 ) 112 113 // If we don't explicitly set the log-level or debug flag(-D) then 114 // turn on debug mode 115 foundIt := false 116 for _, a := range arg { 117 if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") { 118 foundIt = true 119 } 120 } 121 if !foundIt { 122 args = append(args, "--debug") 123 } 124 125 if d.storageDriver != "" { 126 args = append(args, "--storage-driver", d.storageDriver) 127 } 128 if d.execDriver != "" { 129 args = append(args, "--exec-driver", d.execDriver) 130 } 131 132 args = append(args, arg...) 133 d.cmd = exec.Command(dockerBinary, args...) 134 135 d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) 136 if err != nil { 137 d.c.Fatalf("[%s] Could not create %s/docker.log: %v", d.id, d.folder, err) 138 } 139 140 d.cmd.Stdout = d.logFile 141 d.cmd.Stderr = d.logFile 142 143 if err := d.cmd.Start(); err != nil { 144 return fmt.Errorf("[%s] could not start daemon container: %v", d.id, err) 145 } 146 147 wait := make(chan error) 148 149 go func() { 150 wait <- d.cmd.Wait() 151 d.c.Logf("[%s] exiting daemon", d.id) 152 close(wait) 153 }() 154 155 d.wait = wait 156 157 tick := time.Tick(500 * time.Millisecond) 158 // make sure daemon is ready to receive requests 159 startTime := time.Now().Unix() 160 for { 161 d.c.Logf("[%s] waiting for daemon to start", d.id) 162 if time.Now().Unix()-startTime > 5 { 163 // After 5 seconds, give up 164 return fmt.Errorf("[%s] Daemon exited and never started", d.id) 165 } 166 select { 167 case <-time.After(2 * time.Second): 168 return fmt.Errorf("[%s] timeout: daemon does not respond", d.id) 169 case <-tick: 170 c, err := net.Dial("unix", filepath.Join(d.folder, "docker.sock")) 171 if err != nil { 172 continue 173 } 174 175 client := httputil.NewClientConn(c, nil) 176 defer client.Close() 177 178 req, err := http.NewRequest("GET", "/_ping", nil) 179 if err != nil { 180 d.c.Fatalf("[%s] could not create new request: %v", d.id, err) 181 } 182 183 resp, err := client.Do(req) 184 if err != nil { 185 continue 186 } 187 if resp.StatusCode != http.StatusOK { 188 d.c.Logf("[%s] received status != 200 OK: %s", d.id, resp.Status) 189 } 190 191 d.c.Logf("[%s] daemon started", d.id) 192 return nil 193 } 194 } 195 } 196 197 // StartWithBusybox will first start the daemon with Daemon.Start() 198 // then save the busybox image from the main daemon and load it into this Daemon instance. 199 func (d *Daemon) StartWithBusybox(arg ...string) error { 200 if err := d.Start(arg...); err != nil { 201 return err 202 } 203 bb := filepath.Join(d.folder, "busybox.tar") 204 if _, err := os.Stat(bb); err != nil { 205 if !os.IsNotExist(err) { 206 return fmt.Errorf("unexpected error on busybox.tar stat: %v", err) 207 } 208 // saving busybox image from main daemon 209 if err := exec.Command(dockerBinary, "save", "--output", bb, "busybox:latest").Run(); err != nil { 210 return fmt.Errorf("could not save busybox image: %v", err) 211 } 212 } 213 // loading busybox image to this daemon 214 if _, err := d.Cmd("load", "--input", bb); err != nil { 215 return fmt.Errorf("could not load busybox image: %v", err) 216 } 217 if err := os.Remove(bb); err != nil { 218 d.c.Logf("Could not remove %s: %v", bb, err) 219 } 220 return nil 221 } 222 223 // Stop will send a SIGINT every second and wait for the daemon to stop. 224 // If it timeouts, a SIGKILL is sent. 225 // Stop will not delete the daemon directory. If a purged daemon is needed, 226 // instantiate a new one with NewDaemon. 227 func (d *Daemon) Stop() error { 228 if d.cmd == nil || d.wait == nil { 229 return errors.New("daemon not started") 230 } 231 232 defer func() { 233 d.logFile.Close() 234 d.cmd = nil 235 }() 236 237 i := 1 238 tick := time.Tick(time.Second) 239 240 if err := d.cmd.Process.Signal(os.Interrupt); err != nil { 241 return fmt.Errorf("could not send signal: %v", err) 242 } 243 out1: 244 for { 245 select { 246 case err := <-d.wait: 247 return err 248 case <-time.After(15 * time.Second): 249 // time for stopping jobs and run onShutdown hooks 250 d.c.Log("timeout") 251 break out1 252 } 253 } 254 255 out2: 256 for { 257 select { 258 case err := <-d.wait: 259 return err 260 case <-tick: 261 i++ 262 if i > 4 { 263 d.c.Logf("tried to interrupt daemon for %d times, now try to kill it", i) 264 break out2 265 } 266 d.c.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid) 267 if err := d.cmd.Process.Signal(os.Interrupt); err != nil { 268 return fmt.Errorf("could not send signal: %v", err) 269 } 270 } 271 } 272 273 if err := d.cmd.Process.Kill(); err != nil { 274 d.c.Logf("Could not kill daemon: %v", err) 275 return err 276 } 277 278 return nil 279 } 280 281 // Restart will restart the daemon by first stopping it and then starting it. 282 func (d *Daemon) Restart(arg ...string) error { 283 d.Stop() 284 return d.Start(arg...) 285 } 286 287 func (d *Daemon) sock() string { 288 return fmt.Sprintf("unix://%s/docker.sock", d.folder) 289 } 290 291 // Cmd will execute a docker CLI command against this Daemon. 292 // Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version 293 func (d *Daemon) Cmd(name string, arg ...string) (string, error) { 294 args := []string{"--host", d.sock(), name} 295 args = append(args, arg...) 296 c := exec.Command(dockerBinary, args...) 297 b, err := c.CombinedOutput() 298 return string(b), err 299 } 300 301 // CmdWithArgs will execute a docker CLI command against a daemon with the 302 // given additional arguments 303 func (d *Daemon) CmdWithArgs(daemonArgs []string, name string, arg ...string) (string, error) { 304 args := append(daemonArgs, name) 305 args = append(args, arg...) 306 c := exec.Command(dockerBinary, args...) 307 b, err := c.CombinedOutput() 308 return string(b), err 309 } 310 311 // LogfileName returns the path the the daemon's log file 312 func (d *Daemon) LogfileName() string { 313 return d.logFile.Name() 314 } 315 316 func daemonHost() string { 317 daemonURLStr := "unix://" + opts.DefaultUnixSocket 318 if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" { 319 daemonURLStr = daemonHostVar 320 } 321 return daemonURLStr 322 } 323 324 func sockConn(timeout time.Duration) (net.Conn, error) { 325 daemon := daemonHost() 326 daemonURL, err := url.Parse(daemon) 327 if err != nil { 328 return nil, fmt.Errorf("could not parse url %q: %v", daemon, err) 329 } 330 331 var c net.Conn 332 switch daemonURL.Scheme { 333 case "unix": 334 return net.DialTimeout(daemonURL.Scheme, daemonURL.Path, timeout) 335 case "tcp": 336 return net.DialTimeout(daemonURL.Scheme, daemonURL.Host, timeout) 337 default: 338 return c, fmt.Errorf("unknown scheme %v (%s)", daemonURL.Scheme, daemon) 339 } 340 } 341 342 func sockRequest(method, endpoint string, data interface{}) (int, []byte, error) { 343 jsonData := bytes.NewBuffer(nil) 344 if err := json.NewEncoder(jsonData).Encode(data); err != nil { 345 return -1, nil, err 346 } 347 348 res, body, err := sockRequestRaw(method, endpoint, jsonData, "application/json") 349 if err != nil { 350 return -1, nil, err 351 } 352 b, err := readBody(body) 353 return res.StatusCode, b, err 354 } 355 356 func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) { 357 c, err := sockConn(time.Duration(10 * time.Second)) 358 if err != nil { 359 return nil, nil, fmt.Errorf("could not dial docker daemon: %v", err) 360 } 361 362 client := httputil.NewClientConn(c, nil) 363 364 req, err := http.NewRequest(method, endpoint, data) 365 if err != nil { 366 client.Close() 367 return nil, nil, fmt.Errorf("could not create new request: %v", err) 368 } 369 370 if ct != "" { 371 req.Header.Set("Content-Type", ct) 372 } 373 374 resp, err := client.Do(req) 375 if err != nil { 376 client.Close() 377 return nil, nil, fmt.Errorf("could not perform request: %v", err) 378 } 379 body := ioutils.NewReadCloserWrapper(resp.Body, func() error { 380 defer resp.Body.Close() 381 return client.Close() 382 }) 383 384 return resp, body, nil 385 } 386 387 func readBody(b io.ReadCloser) ([]byte, error) { 388 defer b.Close() 389 return ioutil.ReadAll(b) 390 } 391 392 func deleteContainer(container string) error { 393 container = strings.TrimSpace(strings.Replace(container, "\n", " ", -1)) 394 rmArgs := strings.Split(fmt.Sprintf("rm -fv %v", container), " ") 395 exitCode, err := runCommand(exec.Command(dockerBinary, rmArgs...)) 396 // set error manually if not set 397 if exitCode != 0 && err == nil { 398 err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero") 399 } 400 401 return err 402 } 403 404 func getAllContainers() (string, error) { 405 getContainersCmd := exec.Command(dockerBinary, "ps", "-q", "-a") 406 out, exitCode, err := runCommandWithOutput(getContainersCmd) 407 if exitCode != 0 && err == nil { 408 err = fmt.Errorf("failed to get a list of containers: %v\n", out) 409 } 410 411 return out, err 412 } 413 414 func deleteAllContainers() error { 415 containers, err := getAllContainers() 416 if err != nil { 417 fmt.Println(containers) 418 return err 419 } 420 421 if err = deleteContainer(containers); err != nil { 422 return err 423 } 424 return nil 425 } 426 427 var protectedImages = map[string]struct{}{} 428 429 func init() { 430 out, err := exec.Command(dockerBinary, "images").CombinedOutput() 431 if err != nil { 432 panic(err) 433 } 434 lines := strings.Split(string(out), "\n")[1:] 435 for _, l := range lines { 436 if l == "" { 437 continue 438 } 439 fields := strings.Fields(l) 440 imgTag := fields[0] + ":" + fields[1] 441 // just for case if we have dangling images in tested daemon 442 if imgTag != "<none>:<none>" { 443 protectedImages[imgTag] = struct{}{} 444 } 445 } 446 } 447 448 func deleteAllImages() error { 449 out, err := exec.Command(dockerBinary, "images").CombinedOutput() 450 if err != nil { 451 return err 452 } 453 lines := strings.Split(string(out), "\n")[1:] 454 var imgs []string 455 for _, l := range lines { 456 if l == "" { 457 continue 458 } 459 fields := strings.Fields(l) 460 imgTag := fields[0] + ":" + fields[1] 461 if _, ok := protectedImages[imgTag]; !ok { 462 if fields[0] == "<none>" { 463 imgs = append(imgs, fields[2]) 464 continue 465 } 466 imgs = append(imgs, imgTag) 467 } 468 } 469 if len(imgs) == 0 { 470 return nil 471 } 472 args := append([]string{"rmi", "-f"}, imgs...) 473 if err := exec.Command(dockerBinary, args...).Run(); err != nil { 474 return err 475 } 476 return nil 477 } 478 479 func getPausedContainers() (string, error) { 480 getPausedContainersCmd := exec.Command(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") 481 out, exitCode, err := runCommandWithOutput(getPausedContainersCmd) 482 if exitCode != 0 && err == nil { 483 err = fmt.Errorf("failed to get a list of paused containers: %v\n", out) 484 } 485 486 return out, err 487 } 488 489 func getSliceOfPausedContainers() ([]string, error) { 490 out, err := getPausedContainers() 491 if err == nil { 492 if len(out) == 0 { 493 return nil, err 494 } 495 slice := strings.Split(strings.TrimSpace(out), "\n") 496 return slice, err 497 } 498 return []string{out}, err 499 } 500 501 func unpauseContainer(container string) error { 502 unpauseCmd := exec.Command(dockerBinary, "unpause", container) 503 exitCode, err := runCommand(unpauseCmd) 504 if exitCode != 0 && err == nil { 505 err = fmt.Errorf("failed to unpause container") 506 } 507 508 return nil 509 } 510 511 func unpauseAllContainers() error { 512 containers, err := getPausedContainers() 513 if err != nil { 514 fmt.Println(containers) 515 return err 516 } 517 518 containers = strings.Replace(containers, "\n", " ", -1) 519 containers = strings.Trim(containers, " ") 520 containerList := strings.Split(containers, " ") 521 522 for _, value := range containerList { 523 if err = unpauseContainer(value); err != nil { 524 return err 525 } 526 } 527 528 return nil 529 } 530 531 func deleteImages(images ...string) error { 532 args := []string{"rmi", "-f"} 533 args = append(args, images...) 534 rmiCmd := exec.Command(dockerBinary, args...) 535 exitCode, err := runCommand(rmiCmd) 536 // set error manually if not set 537 if exitCode != 0 && err == nil { 538 err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero") 539 } 540 return err 541 } 542 543 func imageExists(image string) error { 544 inspectCmd := exec.Command(dockerBinary, "inspect", image) 545 exitCode, err := runCommand(inspectCmd) 546 if exitCode != 0 && err == nil { 547 err = fmt.Errorf("couldn't find image %q", image) 548 } 549 return err 550 } 551 552 func pullImageIfNotExist(image string) (err error) { 553 if err := imageExists(image); err != nil { 554 pullCmd := exec.Command(dockerBinary, "pull", image) 555 _, exitCode, err := runCommandWithOutput(pullCmd) 556 557 if err != nil || exitCode != 0 { 558 err = fmt.Errorf("image %q wasn't found locally and it couldn't be pulled: %s", image, err) 559 } 560 } 561 return 562 } 563 564 func dockerCmdWithError(c *check.C, args ...string) (string, int, error) { 565 return runCommandWithOutput(exec.Command(dockerBinary, args...)) 566 } 567 568 func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) { 569 stdout, stderr, status, err := runCommandWithStdoutStderr(exec.Command(dockerBinary, args...)) 570 c.Assert(err, check.IsNil, check.Commentf("%q failed with errors: %s, %v", strings.Join(args, " "), stderr, err)) 571 return stdout, stderr, status 572 } 573 574 func dockerCmd(c *check.C, args ...string) (string, int) { 575 out, status, err := runCommandWithOutput(exec.Command(dockerBinary, args...)) 576 c.Assert(err, check.IsNil, check.Commentf("%q failed with errors: %s, %v", strings.Join(args, " "), out, err)) 577 return out, status 578 } 579 580 // execute a docker command with a timeout 581 func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) { 582 out, status, err := runCommandWithOutputAndTimeout(exec.Command(dockerBinary, args...), timeout) 583 if err != nil { 584 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 585 } 586 return out, status, err 587 } 588 589 // execute a docker command in a directory 590 func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error) { 591 dockerCommand := exec.Command(dockerBinary, args...) 592 dockerCommand.Dir = path 593 out, status, err := runCommandWithOutput(dockerCommand) 594 if err != nil { 595 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 596 } 597 return out, status, err 598 } 599 600 // execute a docker command in a directory with a timeout 601 func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) (string, int, error) { 602 dockerCommand := exec.Command(dockerBinary, args...) 603 dockerCommand.Dir = path 604 out, status, err := runCommandWithOutputAndTimeout(dockerCommand, timeout) 605 if err != nil { 606 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 607 } 608 return out, status, err 609 } 610 611 func findContainerIP(c *check.C, id string, vargs ...string) string { 612 args := append(vargs, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id) 613 cmd := exec.Command(dockerBinary, args...) 614 out, _, err := runCommandWithOutput(cmd) 615 if err != nil { 616 c.Fatal(err, out) 617 } 618 619 return strings.Trim(out, " \r\n'") 620 } 621 622 func (d *Daemon) findContainerIP(id string) string { 623 return findContainerIP(d.c, id, "--host", d.sock()) 624 } 625 626 func getContainerCount() (int, error) { 627 const containers = "Containers:" 628 629 cmd := exec.Command(dockerBinary, "info") 630 out, _, err := runCommandWithOutput(cmd) 631 if err != nil { 632 return 0, err 633 } 634 635 lines := strings.Split(out, "\n") 636 for _, line := range lines { 637 if strings.Contains(line, containers) { 638 output := strings.TrimSpace(line) 639 output = strings.TrimLeft(output, containers) 640 output = strings.Trim(output, " ") 641 containerCount, err := strconv.Atoi(output) 642 if err != nil { 643 return 0, err 644 } 645 return containerCount, nil 646 } 647 } 648 return 0, fmt.Errorf("couldn't find the Container count in the output") 649 } 650 651 // FakeContext creates directories that can be used as a build context 652 type FakeContext struct { 653 Dir string 654 } 655 656 // Add a file at a path, creating directories where necessary 657 func (f *FakeContext) Add(file, content string) error { 658 return f.addFile(file, []byte(content)) 659 } 660 661 func (f *FakeContext) addFile(file string, content []byte) error { 662 filepath := path.Join(f.Dir, file) 663 dirpath := path.Dir(filepath) 664 if dirpath != "." { 665 if err := os.MkdirAll(dirpath, 0755); err != nil { 666 return err 667 } 668 } 669 return ioutil.WriteFile(filepath, content, 0644) 670 671 } 672 673 // Delete a file at a path 674 func (f *FakeContext) Delete(file string) error { 675 filepath := path.Join(f.Dir, file) 676 return os.RemoveAll(filepath) 677 } 678 679 // Close deletes the context 680 func (f *FakeContext) Close() error { 681 return os.RemoveAll(f.Dir) 682 } 683 684 func fakeContextFromNewTempDir() (*FakeContext, error) { 685 tmp, err := ioutil.TempDir("", "fake-context") 686 if err != nil { 687 return nil, err 688 } 689 if err := os.Chmod(tmp, 0755); err != nil { 690 return nil, err 691 } 692 return fakeContextFromDir(tmp), nil 693 } 694 695 func fakeContextFromDir(dir string) *FakeContext { 696 return &FakeContext{dir} 697 } 698 699 func fakeContextWithFiles(files map[string]string) (*FakeContext, error) { 700 ctx, err := fakeContextFromNewTempDir() 701 if err != nil { 702 return nil, err 703 } 704 for file, content := range files { 705 if err := ctx.Add(file, content); err != nil { 706 ctx.Close() 707 return nil, err 708 } 709 } 710 return ctx, nil 711 } 712 713 func fakeContextAddDockerfile(ctx *FakeContext, dockerfile string) error { 714 if err := ctx.Add("Dockerfile", dockerfile); err != nil { 715 ctx.Close() 716 return err 717 } 718 return nil 719 } 720 721 func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) { 722 ctx, err := fakeContextWithFiles(files) 723 if err != nil { 724 return nil, err 725 } 726 if err := fakeContextAddDockerfile(ctx, dockerfile); err != nil { 727 return nil, err 728 } 729 return ctx, nil 730 } 731 732 // FakeStorage is a static file server. It might be running locally or remotely 733 // on test host. 734 type FakeStorage interface { 735 Close() error 736 URL() string 737 CtxDir() string 738 } 739 740 func fakeBinaryStorage(archives map[string]*bytes.Buffer) (FakeStorage, error) { 741 ctx, err := fakeContextFromNewTempDir() 742 if err != nil { 743 return nil, err 744 } 745 for name, content := range archives { 746 if err := ctx.addFile(name, content.Bytes()); err != nil { 747 return nil, err 748 } 749 } 750 return fakeStorageWithContext(ctx) 751 } 752 753 // fakeStorage returns either a local or remote (at daemon machine) file server 754 func fakeStorage(files map[string]string) (FakeStorage, error) { 755 ctx, err := fakeContextWithFiles(files) 756 if err != nil { 757 return nil, err 758 } 759 return fakeStorageWithContext(ctx) 760 } 761 762 // fakeStorageWithContext returns either a local or remote (at daemon machine) file server 763 func fakeStorageWithContext(ctx *FakeContext) (FakeStorage, error) { 764 if isLocalDaemon { 765 return newLocalFakeStorage(ctx) 766 } 767 return newRemoteFileServer(ctx) 768 } 769 770 // localFileStorage is a file storage on the running machine 771 type localFileStorage struct { 772 *FakeContext 773 *httptest.Server 774 } 775 776 func (s *localFileStorage) URL() string { 777 return s.Server.URL 778 } 779 780 func (s *localFileStorage) CtxDir() string { 781 return s.FakeContext.Dir 782 } 783 784 func (s *localFileStorage) Close() error { 785 defer s.Server.Close() 786 return s.FakeContext.Close() 787 } 788 789 func newLocalFakeStorage(ctx *FakeContext) (*localFileStorage, error) { 790 handler := http.FileServer(http.Dir(ctx.Dir)) 791 server := httptest.NewServer(handler) 792 return &localFileStorage{ 793 FakeContext: ctx, 794 Server: server, 795 }, nil 796 } 797 798 // remoteFileServer is a containerized static file server started on the remote 799 // testing machine to be used in URL-accepting docker build functionality. 800 type remoteFileServer struct { 801 host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712 802 container string 803 image string 804 ctx *FakeContext 805 } 806 807 func (f *remoteFileServer) URL() string { 808 u := url.URL{ 809 Scheme: "http", 810 Host: f.host} 811 return u.String() 812 } 813 814 func (f *remoteFileServer) CtxDir() string { 815 return f.ctx.Dir 816 } 817 818 func (f *remoteFileServer) Close() error { 819 defer func() { 820 if f.ctx != nil { 821 f.ctx.Close() 822 } 823 if f.image != "" { 824 deleteImages(f.image) 825 } 826 }() 827 if f.container == "" { 828 return nil 829 } 830 return deleteContainer(f.container) 831 } 832 833 func newRemoteFileServer(ctx *FakeContext) (*remoteFileServer, error) { 834 var ( 835 image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10))) 836 container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10))) 837 ) 838 839 // Build the image 840 if err := fakeContextAddDockerfile(ctx, `FROM httpserver 841 COPY . /static`); err != nil { 842 return nil, fmt.Errorf("Cannot add Dockerfile to context: %v", err) 843 } 844 if _, err := buildImageFromContext(image, ctx, false); err != nil { 845 return nil, fmt.Errorf("failed building file storage container image: %v", err) 846 } 847 848 // Start the container 849 runCmd := exec.Command(dockerBinary, "run", "-d", "-P", "--name", container, image) 850 if out, ec, err := runCommandWithOutput(runCmd); err != nil { 851 return nil, fmt.Errorf("failed to start file storage container. ec=%v\nout=%s\nerr=%v", ec, out, err) 852 } 853 854 // Find out the system assigned port 855 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "port", container, "80/tcp")) 856 if err != nil { 857 return nil, fmt.Errorf("failed to find container port: err=%v\nout=%s", err, out) 858 } 859 860 return &remoteFileServer{ 861 container: container, 862 image: image, 863 host: strings.Trim(out, "\n"), 864 ctx: ctx}, nil 865 } 866 867 func inspectFieldAndMarshall(name, field string, output interface{}) error { 868 str, err := inspectFieldJSON(name, field) 869 if err != nil { 870 return err 871 } 872 873 return json.Unmarshal([]byte(str), output) 874 } 875 876 func inspectFilter(name, filter string) (string, error) { 877 format := fmt.Sprintf("{{%s}}", filter) 878 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 879 out, exitCode, err := runCommandWithOutput(inspectCmd) 880 if err != nil || exitCode != 0 { 881 return "", fmt.Errorf("failed to inspect container %s: %s", name, out) 882 } 883 return strings.TrimSpace(out), nil 884 } 885 886 func inspectField(name, field string) (string, error) { 887 return inspectFilter(name, fmt.Sprintf(".%s", field)) 888 } 889 890 func inspectFieldJSON(name, field string) (string, error) { 891 return inspectFilter(name, fmt.Sprintf("json .%s", field)) 892 } 893 894 func inspectFieldMap(name, path, field string) (string, error) { 895 return inspectFilter(name, fmt.Sprintf("index .%s %q", path, field)) 896 } 897 898 func inspectMountSourceField(name, destination string) (string, error) { 899 m, err := inspectMountPoint(name, destination) 900 if err != nil { 901 return "", err 902 } 903 return m.Source, nil 904 } 905 906 func inspectMountPoint(name, destination string) (types.MountPoint, error) { 907 out, err := inspectFieldJSON(name, "Mounts") 908 if err != nil { 909 return types.MountPoint{}, err 910 } 911 912 return inspectMountPointJSON(out, destination) 913 } 914 915 var errMountNotFound = errors.New("mount point not found") 916 917 func inspectMountPointJSON(j, destination string) (types.MountPoint, error) { 918 var mp []types.MountPoint 919 if err := unmarshalJSON([]byte(j), &mp); err != nil { 920 return types.MountPoint{}, err 921 } 922 923 var m *types.MountPoint 924 for _, c := range mp { 925 if c.Destination == destination { 926 m = &c 927 break 928 } 929 } 930 931 if m == nil { 932 return types.MountPoint{}, errMountNotFound 933 } 934 935 return *m, nil 936 } 937 938 func getIDByName(name string) (string, error) { 939 return inspectField(name, "Id") 940 } 941 942 // getContainerState returns the exit code of the container 943 // and true if it's running 944 // the exit code should be ignored if it's running 945 func getContainerState(c *check.C, id string) (int, bool, error) { 946 var ( 947 exitStatus int 948 running bool 949 ) 950 out, exitCode := dockerCmd(c, "inspect", "--format={{.State.Running}} {{.State.ExitCode}}", id) 951 if exitCode != 0 { 952 return 0, false, fmt.Errorf("%q doesn't exist: %s", id, out) 953 } 954 955 out = strings.Trim(out, "\n") 956 splitOutput := strings.Split(out, " ") 957 if len(splitOutput) != 2 { 958 return 0, false, fmt.Errorf("failed to get container state: output is broken") 959 } 960 if splitOutput[0] == "true" { 961 running = true 962 } 963 if n, err := strconv.Atoi(splitOutput[1]); err == nil { 964 exitStatus = n 965 } else { 966 return 0, false, fmt.Errorf("failed to get container state: couldn't parse integer") 967 } 968 969 return exitStatus, running, nil 970 } 971 972 func buildImageCmd(name, dockerfile string, useCache bool) *exec.Cmd { 973 args := []string{"-D", "build", "-t", name} 974 if !useCache { 975 args = append(args, "--no-cache") 976 } 977 args = append(args, "-") 978 buildCmd := exec.Command(dockerBinary, args...) 979 buildCmd.Stdin = strings.NewReader(dockerfile) 980 return buildCmd 981 982 } 983 984 func buildImageWithOut(name, dockerfile string, useCache bool) (string, string, error) { 985 buildCmd := buildImageCmd(name, dockerfile, useCache) 986 out, exitCode, err := runCommandWithOutput(buildCmd) 987 if err != nil || exitCode != 0 { 988 return "", out, fmt.Errorf("failed to build the image: %s", out) 989 } 990 id, err := getIDByName(name) 991 if err != nil { 992 return "", out, err 993 } 994 return id, out, nil 995 } 996 997 func buildImageWithStdoutStderr(name, dockerfile string, useCache bool) (string, string, string, error) { 998 buildCmd := buildImageCmd(name, dockerfile, useCache) 999 stdout, stderr, exitCode, err := runCommandWithStdoutStderr(buildCmd) 1000 if err != nil || exitCode != 0 { 1001 return "", stdout, stderr, fmt.Errorf("failed to build the image: %s", stdout) 1002 } 1003 id, err := getIDByName(name) 1004 if err != nil { 1005 return "", stdout, stderr, err 1006 } 1007 return id, stdout, stderr, nil 1008 } 1009 1010 func buildImage(name, dockerfile string, useCache bool) (string, error) { 1011 id, _, err := buildImageWithOut(name, dockerfile, useCache) 1012 return id, err 1013 } 1014 1015 func buildImageFromContext(name string, ctx *FakeContext, useCache bool) (string, error) { 1016 args := []string{"build", "-t", name} 1017 if !useCache { 1018 args = append(args, "--no-cache") 1019 } 1020 args = append(args, ".") 1021 buildCmd := exec.Command(dockerBinary, args...) 1022 buildCmd.Dir = ctx.Dir 1023 out, exitCode, err := runCommandWithOutput(buildCmd) 1024 if err != nil || exitCode != 0 { 1025 return "", fmt.Errorf("failed to build the image: %s", out) 1026 } 1027 return getIDByName(name) 1028 } 1029 1030 func buildImageFromPath(name, path string, useCache bool) (string, error) { 1031 args := []string{"build", "-t", name} 1032 if !useCache { 1033 args = append(args, "--no-cache") 1034 } 1035 args = append(args, path) 1036 buildCmd := exec.Command(dockerBinary, args...) 1037 out, exitCode, err := runCommandWithOutput(buildCmd) 1038 if err != nil || exitCode != 0 { 1039 return "", fmt.Errorf("failed to build the image: %s", out) 1040 } 1041 return getIDByName(name) 1042 } 1043 1044 type gitServer interface { 1045 URL() string 1046 Close() error 1047 } 1048 1049 type localGitServer struct { 1050 *httptest.Server 1051 } 1052 1053 func (r *localGitServer) Close() error { 1054 r.Server.Close() 1055 return nil 1056 } 1057 1058 func (r *localGitServer) URL() string { 1059 return r.Server.URL 1060 } 1061 1062 type fakeGit struct { 1063 root string 1064 server gitServer 1065 RepoURL string 1066 } 1067 1068 func (g *fakeGit) Close() { 1069 g.server.Close() 1070 os.RemoveAll(g.root) 1071 } 1072 1073 func newFakeGit(name string, files map[string]string, enforceLocalServer bool) (*fakeGit, error) { 1074 ctx, err := fakeContextWithFiles(files) 1075 if err != nil { 1076 return nil, err 1077 } 1078 defer ctx.Close() 1079 curdir, err := os.Getwd() 1080 if err != nil { 1081 return nil, err 1082 } 1083 defer os.Chdir(curdir) 1084 1085 if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil { 1086 return nil, fmt.Errorf("error trying to init repo: %s (%s)", err, output) 1087 } 1088 err = os.Chdir(ctx.Dir) 1089 if err != nil { 1090 return nil, err 1091 } 1092 if output, err := exec.Command("git", "config", "user.name", "Fake User").CombinedOutput(); err != nil { 1093 return nil, fmt.Errorf("error trying to set 'user.name': %s (%s)", err, output) 1094 } 1095 if output, err := exec.Command("git", "config", "user.email", "fake.user@example.com").CombinedOutput(); err != nil { 1096 return nil, fmt.Errorf("error trying to set 'user.email': %s (%s)", err, output) 1097 } 1098 if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil { 1099 return nil, fmt.Errorf("error trying to add files to repo: %s (%s)", err, output) 1100 } 1101 if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil { 1102 return nil, fmt.Errorf("error trying to commit to repo: %s (%s)", err, output) 1103 } 1104 1105 root, err := ioutil.TempDir("", "docker-test-git-repo") 1106 if err != nil { 1107 return nil, err 1108 } 1109 repoPath := filepath.Join(root, name+".git") 1110 if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil { 1111 os.RemoveAll(root) 1112 return nil, fmt.Errorf("error trying to clone --bare: %s (%s)", err, output) 1113 } 1114 err = os.Chdir(repoPath) 1115 if err != nil { 1116 os.RemoveAll(root) 1117 return nil, err 1118 } 1119 if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil { 1120 os.RemoveAll(root) 1121 return nil, fmt.Errorf("error trying to git update-server-info: %s (%s)", err, output) 1122 } 1123 err = os.Chdir(curdir) 1124 if err != nil { 1125 os.RemoveAll(root) 1126 return nil, err 1127 } 1128 1129 var server gitServer 1130 if !enforceLocalServer { 1131 // use fakeStorage server, which might be local or remote (at test daemon) 1132 server, err = fakeStorageWithContext(fakeContextFromDir(root)) 1133 if err != nil { 1134 return nil, fmt.Errorf("cannot start fake storage: %v", err) 1135 } 1136 } else { 1137 // always start a local http server on CLI test machin 1138 httpServer := httptest.NewServer(http.FileServer(http.Dir(root))) 1139 server = &localGitServer{httpServer} 1140 } 1141 return &fakeGit{ 1142 root: root, 1143 server: server, 1144 RepoURL: fmt.Sprintf("%s/%s.git", server.URL(), name), 1145 }, nil 1146 } 1147 1148 // Write `content` to the file at path `dst`, creating it if necessary, 1149 // as well as any missing directories. 1150 // The file is truncated if it already exists. 1151 // Call c.Fatal() at the first error. 1152 func writeFile(dst, content string, c *check.C) { 1153 // Create subdirectories if necessary 1154 if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) { 1155 c.Fatal(err) 1156 } 1157 f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) 1158 if err != nil { 1159 c.Fatal(err) 1160 } 1161 defer f.Close() 1162 // Write content (truncate if it exists) 1163 if _, err := io.Copy(f, strings.NewReader(content)); err != nil { 1164 c.Fatal(err) 1165 } 1166 } 1167 1168 // Return the contents of file at path `src`. 1169 // Call c.Fatal() at the first error (including if the file doesn't exist) 1170 func readFile(src string, c *check.C) (content string) { 1171 data, err := ioutil.ReadFile(src) 1172 if err != nil { 1173 c.Fatal(err) 1174 } 1175 1176 return string(data) 1177 } 1178 1179 func containerStorageFile(containerID, basename string) string { 1180 return filepath.Join("/var/lib/docker/containers", containerID, basename) 1181 } 1182 1183 // docker commands that use this function must be run with the '-d' switch. 1184 func runCommandAndReadContainerFile(filename string, cmd *exec.Cmd) ([]byte, error) { 1185 out, _, err := runCommandWithOutput(cmd) 1186 if err != nil { 1187 return nil, fmt.Errorf("%v: %q", err, out) 1188 } 1189 1190 time.Sleep(1 * time.Second) 1191 1192 contID := strings.TrimSpace(out) 1193 1194 return readContainerFile(contID, filename) 1195 } 1196 1197 func readContainerFile(containerID, filename string) ([]byte, error) { 1198 f, err := os.Open(containerStorageFile(containerID, filename)) 1199 if err != nil { 1200 return nil, err 1201 } 1202 defer f.Close() 1203 1204 content, err := ioutil.ReadAll(f) 1205 if err != nil { 1206 return nil, err 1207 } 1208 1209 return content, nil 1210 } 1211 1212 func readContainerFileWithExec(containerID, filename string) ([]byte, error) { 1213 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "exec", containerID, "cat", filename)) 1214 return []byte(out), err 1215 } 1216 1217 // daemonTime provides the current time on the daemon host 1218 func daemonTime(c *check.C) time.Time { 1219 if isLocalDaemon { 1220 return time.Now() 1221 } 1222 1223 status, body, err := sockRequest("GET", "/info", nil) 1224 c.Assert(status, check.Equals, http.StatusOK) 1225 c.Assert(err, check.IsNil) 1226 1227 type infoJSON struct { 1228 SystemTime string 1229 } 1230 var info infoJSON 1231 if err = json.Unmarshal(body, &info); err != nil { 1232 c.Fatalf("unable to unmarshal /info response: %v", err) 1233 } 1234 1235 dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) 1236 if err != nil { 1237 c.Fatal(err) 1238 } 1239 return dt 1240 } 1241 1242 func setupRegistry(c *check.C) *testRegistryV2 { 1243 testRequires(c, RegistryHosting) 1244 reg, err := newTestRegistryV2(c) 1245 if err != nil { 1246 c.Fatal(err) 1247 } 1248 1249 // Wait for registry to be ready to serve requests. 1250 for i := 0; i != 5; i++ { 1251 if err = reg.Ping(); err == nil { 1252 break 1253 } 1254 time.Sleep(100 * time.Millisecond) 1255 } 1256 1257 if err != nil { 1258 c.Fatal("Timeout waiting for test registry to become available") 1259 } 1260 return reg 1261 } 1262 1263 func setupNotary(c *check.C) *testNotary { 1264 testRequires(c, NotaryHosting) 1265 ts, err := newTestNotary(c) 1266 if err != nil { 1267 c.Fatal(err) 1268 } 1269 1270 return ts 1271 } 1272 1273 // appendBaseEnv appends the minimum set of environment variables to exec the 1274 // docker cli binary for testing with correct configuration to the given env 1275 // list. 1276 func appendBaseEnv(env []string) []string { 1277 preserveList := []string{ 1278 // preserve remote test host 1279 "DOCKER_HOST", 1280 1281 // windows: requires preserving SystemRoot, otherwise dial tcp fails 1282 // with "GetAddrInfoW: A non-recoverable error occurred during a database lookup." 1283 "SystemRoot", 1284 } 1285 1286 for _, key := range preserveList { 1287 if val := os.Getenv(key); val != "" { 1288 env = append(env, fmt.Sprintf("%s=%s", key, val)) 1289 } 1290 } 1291 return env 1292 }