github.com/titanous/docker@v1.4.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 "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 out: 210 for { 211 select { 212 case err := <-d.wait: 213 return err 214 case <-time.After(20 * time.Second): 215 d.t.Log("timeout") 216 break out 217 case <-tick: 218 d.t.Logf("Attempt #%d: daemon is still running with pid %d", i+1, d.cmd.Process.Pid) 219 if err := d.cmd.Process.Signal(os.Interrupt); err != nil { 220 return fmt.Errorf("could not send signal: %v", err) 221 } 222 i++ 223 } 224 } 225 226 if err := d.cmd.Process.Kill(); err != nil { 227 d.t.Logf("Could not kill daemon: %v", err) 228 return err 229 } 230 231 return nil 232 } 233 234 // Restart will restart the daemon by first stopping it and then starting it. 235 func (d *Daemon) Restart(arg ...string) error { 236 d.Stop() 237 return d.Start(arg...) 238 } 239 240 func (d *Daemon) sock() string { 241 return fmt.Sprintf("unix://%s/docker.sock", d.folder) 242 } 243 244 // Cmd will execute a docker CLI command against this Daemon. 245 // Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version 246 func (d *Daemon) Cmd(name string, arg ...string) (string, error) { 247 args := []string{"--host", d.sock(), name} 248 args = append(args, arg...) 249 c := exec.Command(dockerBinary, args...) 250 b, err := c.CombinedOutput() 251 return string(b), err 252 } 253 254 func sockRequest(method, endpoint string, data interface{}) ([]byte, error) { 255 // FIX: the path to sock should not be hardcoded 256 sock := filepath.Join("/", "var", "run", "docker.sock") 257 c, err := net.DialTimeout("unix", sock, time.Duration(10*time.Second)) 258 if err != nil { 259 return nil, fmt.Errorf("could not dial docker sock at %s: %v", sock, err) 260 } 261 262 client := httputil.NewClientConn(c, nil) 263 defer client.Close() 264 265 jsonData := bytes.NewBuffer(nil) 266 if err := json.NewEncoder(jsonData).Encode(data); err != nil { 267 return nil, err 268 } 269 270 req, err := http.NewRequest(method, endpoint, jsonData) 271 req.Header.Set("Content-Type", "application/json") 272 if err != nil { 273 return nil, fmt.Errorf("could not create new request: %v", err) 274 } 275 276 resp, err := client.Do(req) 277 if err != nil { 278 return nil, fmt.Errorf("could not perform request: %v", err) 279 } 280 defer resp.Body.Close() 281 if resp.StatusCode != http.StatusOK { 282 body, _ := ioutil.ReadAll(resp.Body) 283 return body, fmt.Errorf("received status != 200 OK: %s", resp.Status) 284 } 285 286 return ioutil.ReadAll(resp.Body) 287 } 288 289 func deleteContainer(container string) error { 290 container = strings.Replace(container, "\n", " ", -1) 291 container = strings.Trim(container, " ") 292 killArgs := fmt.Sprintf("kill %v", container) 293 killSplitArgs := strings.Split(killArgs, " ") 294 killCmd := exec.Command(dockerBinary, killSplitArgs...) 295 runCommand(killCmd) 296 rmArgs := fmt.Sprintf("rm -v %v", container) 297 rmSplitArgs := strings.Split(rmArgs, " ") 298 rmCmd := exec.Command(dockerBinary, rmSplitArgs...) 299 exitCode, err := runCommand(rmCmd) 300 // set error manually if not set 301 if exitCode != 0 && err == nil { 302 err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero") 303 } 304 305 return err 306 } 307 308 func getAllContainers() (string, error) { 309 getContainersCmd := exec.Command(dockerBinary, "ps", "-q", "-a") 310 out, exitCode, err := runCommandWithOutput(getContainersCmd) 311 if exitCode != 0 && err == nil { 312 err = fmt.Errorf("failed to get a list of containers: %v\n", out) 313 } 314 315 return out, err 316 } 317 318 func deleteAllContainers() error { 319 containers, err := getAllContainers() 320 if err != nil { 321 fmt.Println(containers) 322 return err 323 } 324 325 if err = deleteContainer(containers); err != nil { 326 return err 327 } 328 return nil 329 } 330 331 func getPausedContainers() (string, error) { 332 getPausedContainersCmd := exec.Command(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") 333 out, exitCode, err := runCommandWithOutput(getPausedContainersCmd) 334 if exitCode != 0 && err == nil { 335 err = fmt.Errorf("failed to get a list of paused containers: %v\n", out) 336 } 337 338 return out, err 339 } 340 341 func unpauseContainer(container string) error { 342 unpauseCmd := exec.Command(dockerBinary, "unpause", container) 343 exitCode, err := runCommand(unpauseCmd) 344 if exitCode != 0 && err == nil { 345 err = fmt.Errorf("failed to unpause container") 346 } 347 348 return nil 349 } 350 351 func unpauseAllContainers() error { 352 containers, err := getPausedContainers() 353 if err != nil { 354 fmt.Println(containers) 355 return err 356 } 357 358 containers = strings.Replace(containers, "\n", " ", -1) 359 containers = strings.Trim(containers, " ") 360 containerList := strings.Split(containers, " ") 361 362 for _, value := range containerList { 363 if err = unpauseContainer(value); err != nil { 364 return err 365 } 366 } 367 368 return nil 369 } 370 371 func deleteImages(images ...string) error { 372 args := make([]string, 1, 2) 373 args[0] = "rmi" 374 args = append(args, images...) 375 rmiCmd := exec.Command(dockerBinary, args...) 376 exitCode, err := runCommand(rmiCmd) 377 // set error manually if not set 378 if exitCode != 0 && err == nil { 379 err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero") 380 } 381 382 return err 383 } 384 385 func imageExists(image string) error { 386 inspectCmd := exec.Command(dockerBinary, "inspect", image) 387 exitCode, err := runCommand(inspectCmd) 388 if exitCode != 0 && err == nil { 389 err = fmt.Errorf("couldn't find image %q", image) 390 } 391 return err 392 } 393 394 func pullImageIfNotExist(image string) (err error) { 395 if err := imageExists(image); err != nil { 396 pullCmd := exec.Command(dockerBinary, "pull", image) 397 _, exitCode, err := runCommandWithOutput(pullCmd) 398 399 if err != nil || exitCode != 0 { 400 err = fmt.Errorf("image %q wasn't found locally and it couldn't be pulled: %s", image, err) 401 } 402 } 403 return 404 } 405 406 func dockerCmd(t *testing.T, args ...string) (string, int, error) { 407 out, status, err := runCommandWithOutput(exec.Command(dockerBinary, args...)) 408 if err != nil { 409 t.Fatalf("%q failed with errors: %s, %v", strings.Join(args, " "), out, err) 410 } 411 return out, status, err 412 } 413 414 // execute a docker ocmmand with a timeout 415 func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) { 416 out, status, err := runCommandWithOutputAndTimeout(exec.Command(dockerBinary, args...), timeout) 417 if err != nil { 418 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 419 } 420 return out, status, err 421 } 422 423 // execute a docker command in a directory 424 func dockerCmdInDir(t *testing.T, path string, args ...string) (string, int, error) { 425 dockerCommand := exec.Command(dockerBinary, args...) 426 dockerCommand.Dir = path 427 out, status, err := runCommandWithOutput(dockerCommand) 428 if err != nil { 429 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 430 } 431 return out, status, err 432 } 433 434 // execute a docker command in a directory with a timeout 435 func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) (string, int, error) { 436 dockerCommand := exec.Command(dockerBinary, args...) 437 dockerCommand.Dir = path 438 out, status, err := runCommandWithOutputAndTimeout(dockerCommand, timeout) 439 if err != nil { 440 return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out) 441 } 442 return out, status, err 443 } 444 445 func findContainerIP(t *testing.T, id string) string { 446 cmd := exec.Command(dockerBinary, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id) 447 out, _, err := runCommandWithOutput(cmd) 448 if err != nil { 449 t.Fatal(err, out) 450 } 451 452 return strings.Trim(out, " \r\n'") 453 } 454 455 func getContainerCount() (int, error) { 456 const containers = "Containers:" 457 458 cmd := exec.Command(dockerBinary, "info") 459 out, _, err := runCommandWithOutput(cmd) 460 if err != nil { 461 return 0, err 462 } 463 464 lines := strings.Split(out, "\n") 465 for _, line := range lines { 466 if strings.Contains(line, containers) { 467 output := stripTrailingCharacters(line) 468 output = strings.TrimLeft(output, containers) 469 output = strings.Trim(output, " ") 470 containerCount, err := strconv.Atoi(output) 471 if err != nil { 472 return 0, err 473 } 474 return containerCount, nil 475 } 476 } 477 return 0, fmt.Errorf("couldn't find the Container count in the output") 478 } 479 480 type FakeContext struct { 481 Dir string 482 } 483 484 func (f *FakeContext) Add(file, content string) error { 485 filepath := path.Join(f.Dir, file) 486 dirpath := path.Dir(filepath) 487 if dirpath != "." { 488 if err := os.MkdirAll(dirpath, 0755); err != nil { 489 return err 490 } 491 } 492 return ioutil.WriteFile(filepath, []byte(content), 0644) 493 } 494 495 func (f *FakeContext) Delete(file string) error { 496 filepath := path.Join(f.Dir, file) 497 return os.RemoveAll(filepath) 498 } 499 500 func (f *FakeContext) Close() error { 501 return os.RemoveAll(f.Dir) 502 } 503 504 func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) { 505 tmp, err := ioutil.TempDir("", "fake-context") 506 if err != nil { 507 return nil, err 508 } 509 if err := os.Chmod(tmp, 0755); err != nil { 510 return nil, err 511 } 512 ctx := &FakeContext{tmp} 513 for file, content := range files { 514 if err := ctx.Add(file, content); err != nil { 515 ctx.Close() 516 return nil, err 517 } 518 } 519 if err := ctx.Add("Dockerfile", dockerfile); err != nil { 520 ctx.Close() 521 return nil, err 522 } 523 return ctx, nil 524 } 525 526 type FakeStorage struct { 527 *FakeContext 528 *httptest.Server 529 } 530 531 func (f *FakeStorage) Close() error { 532 f.Server.Close() 533 return f.FakeContext.Close() 534 } 535 536 func fakeStorage(files map[string]string) (*FakeStorage, error) { 537 tmp, err := ioutil.TempDir("", "fake-storage") 538 if err != nil { 539 return nil, err 540 } 541 ctx := &FakeContext{tmp} 542 for file, content := range files { 543 if err := ctx.Add(file, content); err != nil { 544 ctx.Close() 545 return nil, err 546 } 547 } 548 handler := http.FileServer(http.Dir(ctx.Dir)) 549 server := httptest.NewServer(handler) 550 return &FakeStorage{ 551 FakeContext: ctx, 552 Server: server, 553 }, nil 554 } 555 556 func inspectField(name, field string) (string, error) { 557 format := fmt.Sprintf("{{.%s}}", field) 558 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 559 out, exitCode, err := runCommandWithOutput(inspectCmd) 560 if err != nil || exitCode != 0 { 561 return "", fmt.Errorf("failed to inspect %s: %s", name, out) 562 } 563 return strings.TrimSpace(out), nil 564 } 565 566 func inspectFieldJSON(name, field string) (string, error) { 567 format := fmt.Sprintf("{{json .%s}}", field) 568 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 569 out, exitCode, err := runCommandWithOutput(inspectCmd) 570 if err != nil || exitCode != 0 { 571 return "", fmt.Errorf("failed to inspect %s: %s", name, out) 572 } 573 return strings.TrimSpace(out), nil 574 } 575 576 func inspectFieldMap(name, path, field string) (string, error) { 577 format := fmt.Sprintf("{{index .%s %q}}", path, field) 578 inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) 579 out, exitCode, err := runCommandWithOutput(inspectCmd) 580 if err != nil || exitCode != 0 { 581 return "", fmt.Errorf("failed to inspect %s: %s", name, out) 582 } 583 return strings.TrimSpace(out), nil 584 } 585 586 func getIDByName(name string) (string, error) { 587 return inspectField(name, "Id") 588 } 589 590 // getContainerState returns the exit code of the container 591 // and true if it's running 592 // the exit code should be ignored if it's running 593 func getContainerState(t *testing.T, id string) (int, bool, error) { 594 var ( 595 exitStatus int 596 running bool 597 ) 598 out, exitCode, err := dockerCmd(t, "inspect", "--format={{.State.Running}} {{.State.ExitCode}}", id) 599 if err != nil || exitCode != 0 { 600 return 0, false, fmt.Errorf("%q doesn't exist: %s", id, err) 601 } 602 603 out = strings.Trim(out, "\n") 604 splitOutput := strings.Split(out, " ") 605 if len(splitOutput) != 2 { 606 return 0, false, fmt.Errorf("failed to get container state: output is broken") 607 } 608 if splitOutput[0] == "true" { 609 running = true 610 } 611 if n, err := strconv.Atoi(splitOutput[1]); err == nil { 612 exitStatus = n 613 } else { 614 return 0, false, fmt.Errorf("failed to get container state: couldn't parse integer") 615 } 616 617 return exitStatus, running, nil 618 } 619 620 func buildImageWithOut(name, dockerfile string, useCache bool) (string, string, error) { 621 args := []string{"build", "-t", name} 622 if !useCache { 623 args = append(args, "--no-cache") 624 } 625 args = append(args, "-") 626 buildCmd := exec.Command(dockerBinary, args...) 627 buildCmd.Stdin = strings.NewReader(dockerfile) 628 out, exitCode, err := runCommandWithOutput(buildCmd) 629 if err != nil || exitCode != 0 { 630 return "", out, fmt.Errorf("failed to build the image: %s", out) 631 } 632 id, err := getIDByName(name) 633 if err != nil { 634 return "", out, err 635 } 636 return id, out, nil 637 } 638 639 func buildImageWithStdoutStderr(name, dockerfile string, useCache bool) (string, string, string, error) { 640 args := []string{"build", "-t", name} 641 if !useCache { 642 args = append(args, "--no-cache") 643 } 644 args = append(args, "-") 645 buildCmd := exec.Command(dockerBinary, args...) 646 buildCmd.Stdin = strings.NewReader(dockerfile) 647 stdout, stderr, exitCode, err := runCommandWithStdoutStderr(buildCmd) 648 if err != nil || exitCode != 0 { 649 return "", stdout, stderr, fmt.Errorf("failed to build the image: %s", stdout) 650 } 651 id, err := getIDByName(name) 652 if err != nil { 653 return "", stdout, stderr, err 654 } 655 return id, stdout, stderr, nil 656 } 657 658 func buildImage(name, dockerfile string, useCache bool) (string, error) { 659 id, _, err := buildImageWithOut(name, dockerfile, useCache) 660 return id, err 661 } 662 663 func buildImageFromContext(name string, ctx *FakeContext, useCache bool) (string, error) { 664 args := []string{"build", "-t", name} 665 if !useCache { 666 args = append(args, "--no-cache") 667 } 668 args = append(args, ".") 669 buildCmd := exec.Command(dockerBinary, args...) 670 buildCmd.Dir = ctx.Dir 671 out, exitCode, err := runCommandWithOutput(buildCmd) 672 if err != nil || exitCode != 0 { 673 return "", fmt.Errorf("failed to build the image: %s", out) 674 } 675 return getIDByName(name) 676 } 677 678 func buildImageFromPath(name, path string, useCache bool) (string, error) { 679 args := []string{"build", "-t", name} 680 if !useCache { 681 args = append(args, "--no-cache") 682 } 683 args = append(args, path) 684 buildCmd := exec.Command(dockerBinary, args...) 685 out, exitCode, err := runCommandWithOutput(buildCmd) 686 if err != nil || exitCode != 0 { 687 return "", fmt.Errorf("failed to build the image: %s", out) 688 } 689 return getIDByName(name) 690 } 691 692 type FakeGIT struct { 693 *httptest.Server 694 Root string 695 RepoURL string 696 } 697 698 func (g *FakeGIT) Close() { 699 g.Server.Close() 700 os.RemoveAll(g.Root) 701 } 702 703 func fakeGIT(name string, files map[string]string) (*FakeGIT, error) { 704 tmp, err := ioutil.TempDir("", "fake-git-repo") 705 if err != nil { 706 return nil, err 707 } 708 ctx := &FakeContext{tmp} 709 for file, content := range files { 710 if err := ctx.Add(file, content); err != nil { 711 ctx.Close() 712 return nil, err 713 } 714 } 715 defer ctx.Close() 716 curdir, err := os.Getwd() 717 if err != nil { 718 return nil, err 719 } 720 defer os.Chdir(curdir) 721 722 if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil { 723 return nil, fmt.Errorf("error trying to init repo: %s (%s)", err, output) 724 } 725 err = os.Chdir(ctx.Dir) 726 if err != nil { 727 return nil, err 728 } 729 if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil { 730 return nil, fmt.Errorf("error trying to add files to repo: %s (%s)", err, output) 731 } 732 if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil { 733 return nil, fmt.Errorf("error trying to commit to repo: %s (%s)", err, output) 734 } 735 736 root, err := ioutil.TempDir("", "docker-test-git-repo") 737 if err != nil { 738 return nil, err 739 } 740 repoPath := filepath.Join(root, name+".git") 741 if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil { 742 os.RemoveAll(root) 743 return nil, fmt.Errorf("error trying to clone --bare: %s (%s)", err, output) 744 } 745 err = os.Chdir(repoPath) 746 if err != nil { 747 os.RemoveAll(root) 748 return nil, err 749 } 750 if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil { 751 os.RemoveAll(root) 752 return nil, fmt.Errorf("error trying to git update-server-info: %s (%s)", err, output) 753 } 754 err = os.Chdir(curdir) 755 if err != nil { 756 os.RemoveAll(root) 757 return nil, err 758 } 759 handler := http.FileServer(http.Dir(root)) 760 server := httptest.NewServer(handler) 761 return &FakeGIT{ 762 Server: server, 763 Root: root, 764 RepoURL: fmt.Sprintf("%s/%s.git", server.URL, name), 765 }, nil 766 } 767 768 // Write `content` to the file at path `dst`, creating it if necessary, 769 // as well as any missing directories. 770 // The file is truncated if it already exists. 771 // Call t.Fatal() at the first error. 772 func writeFile(dst, content string, t *testing.T) { 773 // Create subdirectories if necessary 774 if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) { 775 t.Fatal(err) 776 } 777 f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) 778 if err != nil { 779 t.Fatal(err) 780 } 781 // Write content (truncate if it exists) 782 if _, err := io.Copy(f, strings.NewReader(content)); err != nil { 783 t.Fatal(err) 784 } 785 } 786 787 // Return the contents of file at path `src`. 788 // Call t.Fatal() at the first error (including if the file doesn't exist) 789 func readFile(src string, t *testing.T) (content string) { 790 f, err := os.Open(src) 791 if err != nil { 792 t.Fatal(err) 793 } 794 data, err := ioutil.ReadAll(f) 795 if err != nil { 796 t.Fatal(err) 797 } 798 return string(data) 799 } 800 801 func containerStorageFile(containerId, basename string) string { 802 return filepath.Join("/var/lib/docker/containers", containerId, basename) 803 } 804 805 // docker commands that use this function must be run with the '-d' switch. 806 func runCommandAndReadContainerFile(filename string, cmd *exec.Cmd) ([]byte, error) { 807 out, _, err := runCommandWithOutput(cmd) 808 if err != nil { 809 return nil, fmt.Errorf("%v: %q", err, out) 810 } 811 812 time.Sleep(1 * time.Second) 813 814 contID := strings.TrimSpace(out) 815 816 return readContainerFile(contID, filename) 817 } 818 819 func readContainerFile(containerId, filename string) ([]byte, error) { 820 f, err := os.Open(containerStorageFile(containerId, filename)) 821 if err != nil { 822 return nil, err 823 } 824 defer f.Close() 825 826 content, err := ioutil.ReadAll(f) 827 if err != nil { 828 return nil, err 829 } 830 831 return content, nil 832 }