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