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