github.com/nguyentm83/docker@v1.5.0/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 "testing" 22 "time" 23 ) 24 25 // Daemon represents a Docker daemon for the testing framework. 26 type Daemon struct { 27 t *testing.T 28 logFile *os.File 29 folder string 30 stdin io.WriteCloser 31 stdout, stderr io.ReadCloser 32 cmd *exec.Cmd 33 storageDriver string 34 execDriver string 35 wait chan error 36 } 37 38 // NewDaemon returns a Daemon instance to be used for testing. 39 // This will create a directory such as daemon123456789 in the folder specified by $DEST. 40 // The daemon will not automatically start. 41 func NewDaemon(t *testing.T) *Daemon { 42 dest := os.Getenv("DEST") 43 if dest == "" { 44 t.Fatal("Please set the DEST environment variable") 45 } 46 47 dir := filepath.Join(dest, fmt.Sprintf("daemon%d", time.Now().UnixNano()%100000000)) 48 daemonFolder, err := filepath.Abs(dir) 49 if err != nil { 50 t.Fatalf("Could not make %q an absolute path: %v", dir, err) 51 } 52 53 if err := os.MkdirAll(filepath.Join(daemonFolder, "graph"), 0600); err != nil { 54 t.Fatalf("Could not create %s/graph directory", daemonFolder) 55 } 56 57 return &Daemon{ 58 t: t, 59 folder: daemonFolder, 60 storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"), 61 execDriver: os.Getenv("DOCKER_EXECDRIVER"), 62 } 63 } 64 65 // Start will start the daemon and return once it is ready to receive requests. 66 // You can specify additional daemon flags. 67 func (d *Daemon) Start(arg ...string) error { 68 dockerBinary, err := exec.LookPath(dockerBinary) 69 if err != nil { 70 d.t.Fatalf("could not find docker binary in $PATH: %v", err) 71 } 72 73 args := []string{ 74 "--host", d.sock(), 75 "--daemon", 76 "--graph", fmt.Sprintf("%s/graph", d.folder), 77 "--pidfile", fmt.Sprintf("%s/docker.pid", d.folder), 78 } 79 80 // If we don't explicitly set the log-level or debug flag(-D) then 81 // turn on debug mode 82 foundIt := false 83 for _, a := range arg { 84 if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") { 85 foundIt = true 86 } 87 } 88 if !foundIt { 89 args = append(args, "--debug") 90 } 91 92 if d.storageDriver != "" { 93 args = append(args, "--storage-driver", d.storageDriver) 94 } 95 if d.execDriver != "" { 96 args = append(args, "--exec-driver", d.execDriver) 97 } 98 99 args = append(args, arg...) 100 d.cmd = exec.Command(dockerBinary, args...) 101 102 d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) 103 if err != nil { 104 d.t.Fatalf("Could not create %s/docker.log: %v", d.folder, err) 105 } 106 107 d.cmd.Stdout = d.logFile 108 d.cmd.Stderr = d.logFile 109 110 if err := d.cmd.Start(); err != nil { 111 return fmt.Errorf("could not start daemon container: %v", err) 112 } 113 114 wait := make(chan error) 115 116 go func() { 117 wait <- d.cmd.Wait() 118 d.t.Log("exiting daemon") 119 close(wait) 120 }() 121 122 d.wait = wait 123 124 tick := time.Tick(500 * time.Millisecond) 125 // make sure daemon is ready to receive requests 126 startTime := time.Now().Unix() 127 for { 128 d.t.Log("waiting for daemon to start") 129 if time.Now().Unix()-startTime > 5 { 130 // After 5 seconds, give up 131 return errors.New("Daemon exited and never started") 132 } 133 select { 134 case <-time.After(2 * time.Second): 135 return errors.New("timeout: daemon does not respond") 136 case <-tick: 137 c, err := net.Dial("unix", filepath.Join(d.folder, "docker.sock")) 138 if err != nil { 139 continue 140 } 141 142 client := httputil.NewClientConn(c, nil) 143 defer client.Close() 144 145 req, err := http.NewRequest("GET", "/_ping", nil) 146 if err != nil { 147 d.t.Fatalf("could not create new request: %v", err) 148 } 149 150 resp, err := client.Do(req) 151 if err != nil { 152 continue 153 } 154 if resp.StatusCode != http.StatusOK { 155 d.t.Logf("received status != 200 OK: %s", resp.Status) 156 } 157 158 d.t.Log("daemon started") 159 return nil 160 } 161 } 162 } 163 164 // StartWithBusybox will first start the daemon with Daemon.Start() 165 // then save the busybox image from the main daemon and load it into this Daemon instance. 166 func (d *Daemon) StartWithBusybox(arg ...string) error { 167 if err := d.Start(arg...); err != nil { 168 return err 169 } 170 bb := filepath.Join(d.folder, "busybox.tar") 171 if _, err := os.Stat(bb); err != nil { 172 if !os.IsNotExist(err) { 173 return fmt.Errorf("unexpected error on busybox.tar stat: %v", err) 174 } 175 // saving busybox image from main daemon 176 if err := exec.Command(dockerBinary, "save", "--output", bb, "busybox:latest").Run(); err != nil { 177 return fmt.Errorf("could not save busybox image: %v", err) 178 } 179 } 180 // loading busybox image to this daemon 181 if _, err := d.Cmd("load", "--input", bb); err != nil { 182 return fmt.Errorf("could not load busybox image: %v", err) 183 } 184 if err := os.Remove(bb); err != nil { 185 d.t.Logf("Could not remove %s: %v", bb, err) 186 } 187 return nil 188 } 189 190 // Stop will send a SIGINT every second and wait for the daemon to stop. 191 // If it timeouts, a SIGKILL is sent. 192 // Stop will not delete the daemon directory. If a purged daemon is needed, 193 // instantiate a new one with NewDaemon. 194 func (d *Daemon) Stop() error { 195 if d.cmd == nil || d.wait == nil { 196 return errors.New("daemon not started") 197 } 198 199 defer func() { 200 d.logFile.Close() 201 d.cmd = nil 202 }() 203 204 i := 1 205 tick := time.Tick(time.Second) 206 207 if err := d.cmd.Process.Signal(os.Interrupt); err != nil { 208 return fmt.Errorf("could not send signal: %v", err) 209 } 210 out1: 211 for { 212 select { 213 case err := <-d.wait: 214 return err 215 case <-time.After(15 * time.Second): 216 // time for stopping jobs and run onShutdown hooks 217 d.t.Log("timeout") 218 break out1 219 } 220 } 221 222 out2: 223 for { 224 select { 225 case err := <-d.wait: 226 return err 227 case <-tick: 228 i++ 229 if i > 4 { 230 d.t.Logf("tried to interrupt daemon for %d times, now try to kill it", i) 231 break out2 232 } 233 d.t.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid) 234 if err := d.cmd.Process.Signal(os.Interrupt); err != nil { 235 return fmt.Errorf("could not send signal: %v", err) 236 } 237 } 238 } 239 240 if err := d.cmd.Process.Kill(); err != nil { 241 d.t.Logf("Could not kill daemon: %v", err) 242 return err 243 } 244 245 return nil 246 } 247 248 // Restart will restart the daemon by first stopping it and then starting it. 249 func (d *Daemon) Restart(arg ...string) error { 250 d.Stop() 251 return d.Start(arg...) 252 } 253 254 func (d *Daemon) sock() string { 255 return fmt.Sprintf("unix://%s/docker.sock", d.folder) 256 } 257 258 // Cmd will execute a docker CLI command against this Daemon. 259 // Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version 260 func (d *Daemon) Cmd(name string, arg ...string) (string, error) { 261 args := []string{"--host", d.sock(), name} 262 args = append(args, arg...) 263 c := exec.Command(dockerBinary, args...) 264 b, err := c.CombinedOutput() 265 return string(b), err 266 } 267 268 func daemonHost() string { 269 daemonUrlStr := "unix:///var/run/docker.sock" 270 if daemonHostVar := os.Getenv("DOCKER_TEST_HOST"); daemonHostVar != "" { 271 daemonUrlStr = daemonHostVar 272 } 273 return daemonUrlStr 274 } 275 276 func sockRequest(method, endpoint string, data interface{}) ([]byte, error) { 277 jsonData := bytes.NewBuffer(nil) 278 if err := json.NewEncoder(jsonData).Encode(data); err != nil { 279 return nil, err 280 } 281 282 return sockRequestRaw(method, endpoint, jsonData, "application/json") 283 } 284 285 func sockRequestRaw(method, endpoint string, data io.Reader, ct string) ([]byte, error) { 286 daemon := daemonHost() 287 daemonUrl, err := url.Parse(daemon) 288 if err != nil { 289 return nil, fmt.Errorf("could not parse url %q: %v", daemon, err) 290 } 291 292 var c net.Conn 293 switch daemonUrl.Scheme { 294 case "unix": 295 c, err = net.DialTimeout(daemonUrl.Scheme, daemonUrl.Path, time.Duration(10*time.Second)) 296 case "tcp": 297 c, err = net.DialTimeout(daemonUrl.Scheme, daemonUrl.Host, time.Duration(10*time.Second)) 298 default: 299 err = fmt.Errorf("unknown scheme %v", daemonUrl.Scheme) 300 } 301 if err != nil { 302 return nil, fmt.Errorf("could not dial docker daemon at %s: %v", daemon, err) 303 } 304 305 client := httputil.NewClientConn(c, nil) 306 defer client.Close() 307 308 req, err := http.NewRequest(method, endpoint, data) 309 if err != nil { 310 return nil, fmt.Errorf("could not create new request: %v", err) 311 } 312 313 if ct == "" { 314 ct = "application/json" 315 } 316 req.Header.Set("Content-Type", ct) 317 318 resp, err := client.Do(req) 319 if err != nil { 320 return nil, fmt.Errorf("could not perform request: %v", err) 321 } 322 defer resp.Body.Close() 323 if resp.StatusCode != http.StatusOK { 324 body, _ := ioutil.ReadAll(resp.Body) 325 return body, fmt.Errorf("received status != 200 OK: %s", resp.Status) 326 } 327 328 return ioutil.ReadAll(resp.Body) 329 } 330 331 func deleteContainer(container string) error { 332 container = strings.Replace(container, "\n", " ", -1) 333 container = strings.Trim(container, " ") 334 killArgs := fmt.Sprintf("kill %v", container) 335 killSplitArgs := strings.Split(killArgs, " ") 336 killCmd := exec.Command(dockerBinary, killSplitArgs...) 337 runCommand(killCmd) 338 rmArgs := fmt.Sprintf("rm -v %v", container) 339 rmSplitArgs := strings.Split(rmArgs, " ") 340 rmCmd := exec.Command(dockerBinary, rmSplitArgs...) 341 exitCode, err := runCommand(rmCmd) 342 // set error manually if not set 343 if exitCode != 0 && err == nil { 344 err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero") 345 } 346 347 return err 348 } 349 350 func getAllContainers() (string, error) { 351 getContainersCmd := exec.Command(dockerBinary, "ps", "-q", "-a") 352 out, exitCode, err := runCommandWithOutput(getContainersCmd) 353 if exitCode != 0 && err == nil { 354 err = fmt.Errorf("failed to get a list of containers: %v\n", out) 355 } 356 357 return out, err 358 } 359 360 func deleteAllContainers() error { 361 containers, err := getAllContainers() 362 if err != nil { 363 fmt.Println(containers) 364 return err 365 } 366 367 if err = deleteContainer(containers); err != nil { 368 return err 369 } 370 return nil 371 } 372 373 func getPausedContainers() (string, error) { 374 getPausedContainersCmd := exec.Command(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") 375 out, exitCode, err := runCommandWithOutput(getPausedContainersCmd) 376 if exitCode != 0 && err == nil { 377 err = fmt.Errorf("failed to get a list of paused containers: %v\n", out) 378 } 379 380 return out, err 381 } 382 383 func unpauseContainer(container string) error { 384 unpauseCmd := exec.Command(dockerBinary, "unpause", container) 385 exitCode, err := runCommand(unpauseCmd) 386 if exitCode != 0 && err == nil { 387 err = fmt.Errorf("failed to unpause container") 388 } 389 390 return nil 391 } 392 393 func unpauseAllContainers() error { 394 containers, err := getPausedContainers() 395 if err != nil { 396 fmt.Println(containers) 397 return err 398 } 399 400 containers = strings.Replace(containers, "\n", " ", -1) 401 containers = strings.Trim(containers, " ") 402 containerList := strings.Split(containers, " ") 403 404 for _, value := range containerList { 405 if err = unpauseContainer(value); err != nil { 406 return err 407 } 408 } 409 410 return nil 411 } 412 413 func deleteImages(images ...string) error { 414 args := make([]string, 1, 2) 415 args[0] = "rmi" 416 args = append(args, images...) 417 rmiCmd := exec.Command(dockerBinary, args...) 418 exitCode, err := runCommand(rmiCmd) 419 // set error manually if not set 420 if exitCode != 0 && err == nil { 421 err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero") 422 } 423 424 return err 425 } 426 427 func imageExists(image string) error { 428 inspectCmd := exec.Command(dockerBinary, "inspect", image) 429 exitCode, err := runCommand(inspectCmd) 430 if exitCode != 0 && err == nil { 431 err = fmt.Errorf("couldn't find image %q", image) 432 } 433 return err 434 } 435 436 func pullImageIfNotExist(image string) (err error) { 437 if err := imageExists(image); err != nil { 438 pullCmd := exec.Command(dockerBinary, "pull", image) 439 _, exitCode, err := runCommandWithOutput(pullCmd) 440 441 if err != nil || exitCode != 0 { 442 err = fmt.Errorf("image %q wasn't found locally and it couldn't be pulled: %s", image, err) 443 } 444 } 445 return 446 } 447 448 func dockerCmd(t *testing.T, args ...string) (string, int, error) { 449 out, status, err := runCommandWithOutput(exec.Command(dockerBinary, args...)) 450 if err != nil { 451 t.Fatalf("%q failed with errors: %s, %v", strings.Join(args, " "), out, err) 452 } 453 return out, status, err 454 } 455 456 // execute a docker ocmmand with a timeout 457 func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) { 458 out, status, err := runCommandWithOutputAndTimeout(exec.Command(dockerBinary, args...), timeout) 459 if err != nil { 460 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 461 } 462 return out, status, err 463 } 464 465 // execute a docker command in a directory 466 func dockerCmdInDir(t *testing.T, path string, args ...string) (string, int, error) { 467 dockerCommand := exec.Command(dockerBinary, args...) 468 dockerCommand.Dir = path 469 out, status, err := runCommandWithOutput(dockerCommand) 470 if err != nil { 471 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 472 } 473 return out, status, err 474 } 475 476 // execute a docker command in a directory with a timeout 477 func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) (string, int, error) { 478 dockerCommand := exec.Command(dockerBinary, args...) 479 dockerCommand.Dir = path 480 out, status, err := runCommandWithOutputAndTimeout(dockerCommand, timeout) 481 if err != nil { 482 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 483 } 484 return out, status, err 485 } 486 487 func findContainerIP(t *testing.T, id string) string { 488 cmd := exec.Command(dockerBinary, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id) 489 out, _, err := runCommandWithOutput(cmd) 490 if err != nil { 491 t.Fatal(err, out) 492 } 493 494 return strings.Trim(out, " \r\n'") 495 } 496 497 func getContainerCount() (int, error) { 498 const containers = "Containers:" 499 500 cmd := exec.Command(dockerBinary, "info") 501 out, _, err := runCommandWithOutput(cmd) 502 if err != nil { 503 return 0, err 504 } 505 506 lines := strings.Split(out, "\n") 507 for _, line := range lines { 508 if strings.Contains(line, containers) { 509 output := stripTrailingCharacters(line) 510 output = strings.TrimLeft(output, containers) 511 output = strings.Trim(output, " ") 512 containerCount, err := strconv.Atoi(output) 513 if err != nil { 514 return 0, err 515 } 516 return containerCount, nil 517 } 518 } 519 return 0, fmt.Errorf("couldn't find the Container count in the output") 520 } 521 522 type FakeContext struct { 523 Dir string 524 } 525 526 func (f *FakeContext) Add(file, content string) error { 527 filepath := path.Join(f.Dir, file) 528 dirpath := path.Dir(filepath) 529 if dirpath != "." { 530 if err := os.MkdirAll(dirpath, 0755); err != nil { 531 return err 532 } 533 } 534 return ioutil.WriteFile(filepath, []byte(content), 0644) 535 } 536 537 func (f *FakeContext) Delete(file string) error { 538 filepath := path.Join(f.Dir, file) 539 return os.RemoveAll(filepath) 540 } 541 542 func (f *FakeContext) Close() error { 543 return os.RemoveAll(f.Dir) 544 } 545 546 func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) { 547 tmp, err := ioutil.TempDir("", "fake-context") 548 if err != nil { 549 return nil, err 550 } 551 if err := os.Chmod(tmp, 0755); err != nil { 552 return nil, err 553 } 554 ctx := &FakeContext{tmp} 555 for file, content := range files { 556 if err := ctx.Add(file, content); err != nil { 557 ctx.Close() 558 return nil, err 559 } 560 } 561 if err := ctx.Add("Dockerfile", dockerfile); err != nil { 562 ctx.Close() 563 return nil, err 564 } 565 return ctx, nil 566 } 567 568 type FakeStorage struct { 569 *FakeContext 570 *httptest.Server 571 } 572 573 func (f *FakeStorage) Close() error { 574 f.Server.Close() 575 return f.FakeContext.Close() 576 } 577 578 func fakeStorage(files map[string]string) (*FakeStorage, error) { 579 tmp, err := ioutil.TempDir("", "fake-storage") 580 if err != nil { 581 return nil, err 582 } 583 ctx := &FakeContext{tmp} 584 for file, content := range files { 585 if err := ctx.Add(file, content); err != nil { 586 ctx.Close() 587 return nil, err 588 } 589 } 590 handler := http.FileServer(http.Dir(ctx.Dir)) 591 server := httptest.NewServer(handler) 592 return &FakeStorage{ 593 FakeContext: ctx, 594 Server: server, 595 }, nil 596 } 597 598 func inspectField(name, field string) (string, error) { 599 format := fmt.Sprintf("{{.%s}}", field) 600 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 601 out, exitCode, err := runCommandWithOutput(inspectCmd) 602 if err != nil || exitCode != 0 { 603 return "", fmt.Errorf("failed to inspect %s: %s", name, out) 604 } 605 return strings.TrimSpace(out), nil 606 } 607 608 func inspectFieldJSON(name, field string) (string, error) { 609 format := fmt.Sprintf("{{json .%s}}", field) 610 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 611 out, exitCode, err := runCommandWithOutput(inspectCmd) 612 if err != nil || exitCode != 0 { 613 return "", fmt.Errorf("failed to inspect %s: %s", name, out) 614 } 615 return strings.TrimSpace(out), nil 616 } 617 618 func inspectFieldMap(name, path, field string) (string, error) { 619 format := fmt.Sprintf("{{index .%s %q}}", path, field) 620 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 621 out, exitCode, err := runCommandWithOutput(inspectCmd) 622 if err != nil || exitCode != 0 { 623 return "", fmt.Errorf("failed to inspect %s: %s", name, out) 624 } 625 return strings.TrimSpace(out), nil 626 } 627 628 func getIDByName(name string) (string, error) { 629 return inspectField(name, "Id") 630 } 631 632 // getContainerState returns the exit code of the container 633 // and true if it's running 634 // the exit code should be ignored if it's running 635 func getContainerState(t *testing.T, id string) (int, bool, error) { 636 var ( 637 exitStatus int 638 running bool 639 ) 640 out, exitCode, err := dockerCmd(t, "inspect", "--format={{.State.Running}} {{.State.ExitCode}}", id) 641 if err != nil || exitCode != 0 { 642 return 0, false, fmt.Errorf("%q doesn't exist: %s", id, err) 643 } 644 645 out = strings.Trim(out, "\n") 646 splitOutput := strings.Split(out, " ") 647 if len(splitOutput) != 2 { 648 return 0, false, fmt.Errorf("failed to get container state: output is broken") 649 } 650 if splitOutput[0] == "true" { 651 running = true 652 } 653 if n, err := strconv.Atoi(splitOutput[1]); err == nil { 654 exitStatus = n 655 } else { 656 return 0, false, fmt.Errorf("failed to get container state: couldn't parse integer") 657 } 658 659 return exitStatus, running, nil 660 } 661 662 func buildImageWithOut(name, dockerfile string, useCache bool) (string, string, error) { 663 args := []string{"build", "-t", name} 664 if !useCache { 665 args = append(args, "--no-cache") 666 } 667 args = append(args, "-") 668 buildCmd := exec.Command(dockerBinary, args...) 669 buildCmd.Stdin = strings.NewReader(dockerfile) 670 out, exitCode, err := runCommandWithOutput(buildCmd) 671 if err != nil || exitCode != 0 { 672 return "", out, fmt.Errorf("failed to build the image: %s", out) 673 } 674 id, err := getIDByName(name) 675 if err != nil { 676 return "", out, err 677 } 678 return id, out, nil 679 } 680 681 func buildImageWithStdoutStderr(name, dockerfile string, useCache bool) (string, string, string, error) { 682 args := []string{"build", "-t", name} 683 if !useCache { 684 args = append(args, "--no-cache") 685 } 686 args = append(args, "-") 687 buildCmd := exec.Command(dockerBinary, args...) 688 buildCmd.Stdin = strings.NewReader(dockerfile) 689 stdout, stderr, exitCode, err := runCommandWithStdoutStderr(buildCmd) 690 if err != nil || exitCode != 0 { 691 return "", stdout, stderr, fmt.Errorf("failed to build the image: %s", stdout) 692 } 693 id, err := getIDByName(name) 694 if err != nil { 695 return "", stdout, stderr, err 696 } 697 return id, stdout, stderr, nil 698 } 699 700 func buildImage(name, dockerfile string, useCache bool) (string, error) { 701 id, _, err := buildImageWithOut(name, dockerfile, useCache) 702 return id, err 703 } 704 705 func buildImageFromContext(name string, ctx *FakeContext, useCache bool) (string, error) { 706 args := []string{"build", "-t", name} 707 if !useCache { 708 args = append(args, "--no-cache") 709 } 710 args = append(args, ".") 711 buildCmd := exec.Command(dockerBinary, args...) 712 buildCmd.Dir = ctx.Dir 713 out, exitCode, err := runCommandWithOutput(buildCmd) 714 if err != nil || exitCode != 0 { 715 return "", fmt.Errorf("failed to build the image: %s", out) 716 } 717 return getIDByName(name) 718 } 719 720 func buildImageFromPath(name, path string, useCache bool) (string, error) { 721 args := []string{"build", "-t", name} 722 if !useCache { 723 args = append(args, "--no-cache") 724 } 725 args = append(args, path) 726 buildCmd := exec.Command(dockerBinary, args...) 727 out, exitCode, err := runCommandWithOutput(buildCmd) 728 if err != nil || exitCode != 0 { 729 return "", fmt.Errorf("failed to build the image: %s", out) 730 } 731 return getIDByName(name) 732 } 733 734 type FakeGIT struct { 735 *httptest.Server 736 Root string 737 RepoURL string 738 } 739 740 func (g *FakeGIT) Close() { 741 g.Server.Close() 742 os.RemoveAll(g.Root) 743 } 744 745 func fakeGIT(name string, files map[string]string) (*FakeGIT, error) { 746 tmp, err := ioutil.TempDir("", "fake-git-repo") 747 if err != nil { 748 return nil, err 749 } 750 ctx := &FakeContext{tmp} 751 for file, content := range files { 752 if err := ctx.Add(file, content); err != nil { 753 ctx.Close() 754 return nil, err 755 } 756 } 757 defer ctx.Close() 758 curdir, err := os.Getwd() 759 if err != nil { 760 return nil, err 761 } 762 defer os.Chdir(curdir) 763 764 if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil { 765 return nil, fmt.Errorf("error trying to init repo: %s (%s)", err, output) 766 } 767 err = os.Chdir(ctx.Dir) 768 if err != nil { 769 return nil, err 770 } 771 if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil { 772 return nil, fmt.Errorf("error trying to add files to repo: %s (%s)", err, output) 773 } 774 if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil { 775 return nil, fmt.Errorf("error trying to commit to repo: %s (%s)", err, output) 776 } 777 778 root, err := ioutil.TempDir("", "docker-test-git-repo") 779 if err != nil { 780 return nil, err 781 } 782 repoPath := filepath.Join(root, name+".git") 783 if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil { 784 os.RemoveAll(root) 785 return nil, fmt.Errorf("error trying to clone --bare: %s (%s)", err, output) 786 } 787 err = os.Chdir(repoPath) 788 if err != nil { 789 os.RemoveAll(root) 790 return nil, err 791 } 792 if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil { 793 os.RemoveAll(root) 794 return nil, fmt.Errorf("error trying to git update-server-info: %s (%s)", err, output) 795 } 796 err = os.Chdir(curdir) 797 if err != nil { 798 os.RemoveAll(root) 799 return nil, err 800 } 801 handler := http.FileServer(http.Dir(root)) 802 server := httptest.NewServer(handler) 803 return &FakeGIT{ 804 Server: server, 805 Root: root, 806 RepoURL: fmt.Sprintf("%s/%s.git", server.URL, name), 807 }, nil 808 } 809 810 // Write `content` to the file at path `dst`, creating it if necessary, 811 // as well as any missing directories. 812 // The file is truncated if it already exists. 813 // Call t.Fatal() at the first error. 814 func writeFile(dst, content string, t *testing.T) { 815 // Create subdirectories if necessary 816 if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) { 817 t.Fatal(err) 818 } 819 f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) 820 if err != nil { 821 t.Fatal(err) 822 } 823 // Write content (truncate if it exists) 824 if _, err := io.Copy(f, strings.NewReader(content)); err != nil { 825 t.Fatal(err) 826 } 827 } 828 829 // Return the contents of file at path `src`. 830 // Call t.Fatal() at the first error (including if the file doesn't exist) 831 func readFile(src string, t *testing.T) (content string) { 832 f, err := os.Open(src) 833 if err != nil { 834 t.Fatal(err) 835 } 836 data, err := ioutil.ReadAll(f) 837 if err != nil { 838 t.Fatal(err) 839 } 840 return string(data) 841 } 842 843 func containerStorageFile(containerId, basename string) string { 844 return filepath.Join("/var/lib/docker/containers", containerId, basename) 845 } 846 847 // docker commands that use this function must be run with the '-d' switch. 848 func runCommandAndReadContainerFile(filename string, cmd *exec.Cmd) ([]byte, error) { 849 out, _, err := runCommandWithOutput(cmd) 850 if err != nil { 851 return nil, fmt.Errorf("%v: %q", err, out) 852 } 853 854 time.Sleep(1 * time.Second) 855 856 contID := strings.TrimSpace(out) 857 858 return readContainerFile(contID, filename) 859 } 860 861 func readContainerFile(containerId, filename string) ([]byte, error) { 862 f, err := os.Open(containerStorageFile(containerId, filename)) 863 if err != nil { 864 return nil, err 865 } 866 defer f.Close() 867 868 content, err := ioutil.ReadAll(f) 869 if err != nil { 870 return nil, err 871 } 872 873 return content, nil 874 } 875 876 func setupRegistry(t *testing.T) func() { 877 reg, err := newTestRegistryV2(t) 878 if err != nil { 879 t.Fatal(err) 880 } 881 882 // Wait for registry to be ready to serve requests. 883 for i := 0; i != 5; i++ { 884 if err = reg.Ping(); err == nil { 885 break 886 } 887 time.Sleep(100 * time.Millisecond) 888 } 889 890 if err != nil { 891 t.Fatal("Timeout waiting for test registry to become available") 892 } 893 894 return func() { reg.Close() } 895 }