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