github.com/fcwu/docker@v1.4.2-0.20150115145920-2a69ca89f0df/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  }