github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/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  }
   867  
   868  func setupRegistry(t *testing.T) func() {
   869  	reg, err := newTestRegistryV2(t)
   870  	if err != nil {
   871  		t.Fatal(err)
   872  	}
   873  	return func() { reg.Close() }
   874  }