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