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  }