github.com/rentongzhang/docker@v1.8.2-rc1/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  	"time"
    22  
    23  	"github.com/docker/docker/api/types"
    24  	"github.com/docker/docker/opts"
    25  	"github.com/docker/docker/pkg/ioutils"
    26  	"github.com/docker/docker/pkg/stringutils"
    27  	"github.com/go-check/check"
    28  )
    29  
    30  // Daemon represents a Docker daemon for the testing framework.
    31  type Daemon struct {
    32  	// Defaults to "daemon"
    33  	// Useful to set to --daemon or -d for checking backwards compatability
    34  	Command     string
    35  	GlobalFlags []string
    36  
    37  	id             string
    38  	c              *check.C
    39  	logFile        *os.File
    40  	folder         string
    41  	stdin          io.WriteCloser
    42  	stdout, stderr io.ReadCloser
    43  	cmd            *exec.Cmd
    44  	storageDriver  string
    45  	execDriver     string
    46  	wait           chan error
    47  	userlandProxy  bool
    48  }
    49  
    50  func enableUserlandProxy() bool {
    51  	if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" {
    52  		if val, err := strconv.ParseBool(env); err != nil {
    53  			return val
    54  		}
    55  	}
    56  	return true
    57  }
    58  
    59  // NewDaemon returns a Daemon instance to be used for testing.
    60  // This will create a directory such as d123456789 in the folder specified by $DEST.
    61  // The daemon will not automatically start.
    62  func NewDaemon(c *check.C) *Daemon {
    63  	dest := os.Getenv("DEST")
    64  	if dest == "" {
    65  		c.Fatal("Please set the DEST environment variable")
    66  	}
    67  
    68  	id := fmt.Sprintf("d%d", time.Now().UnixNano()%100000000)
    69  	dir := filepath.Join(dest, id)
    70  	daemonFolder, err := filepath.Abs(dir)
    71  	if err != nil {
    72  		c.Fatalf("Could not make %q an absolute path: %v", dir, err)
    73  	}
    74  
    75  	if err := os.MkdirAll(filepath.Join(daemonFolder, "graph"), 0600); err != nil {
    76  		c.Fatalf("Could not create %s/graph directory", daemonFolder)
    77  	}
    78  
    79  	userlandProxy := true
    80  	if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" {
    81  		if val, err := strconv.ParseBool(env); err != nil {
    82  			userlandProxy = val
    83  		}
    84  	}
    85  
    86  	return &Daemon{
    87  		Command:       "daemon",
    88  		id:            id,
    89  		c:             c,
    90  		folder:        daemonFolder,
    91  		storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"),
    92  		execDriver:    os.Getenv("DOCKER_EXECDRIVER"),
    93  		userlandProxy: userlandProxy,
    94  	}
    95  }
    96  
    97  // Start will start the daemon and return once it is ready to receive requests.
    98  // You can specify additional daemon flags.
    99  func (d *Daemon) Start(arg ...string) error {
   100  	dockerBinary, err := exec.LookPath(dockerBinary)
   101  	if err != nil {
   102  		d.c.Fatalf("[%s] could not find docker binary in $PATH: %v", d.id, err)
   103  	}
   104  
   105  	args := append(d.GlobalFlags,
   106  		d.Command,
   107  		"--host", d.sock(),
   108  		"--graph", fmt.Sprintf("%s/graph", d.folder),
   109  		"--pidfile", fmt.Sprintf("%s/docker.pid", d.folder),
   110  		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
   111  	)
   112  
   113  	// If we don't explicitly set the log-level or debug flag(-D) then
   114  	// turn on debug mode
   115  	foundIt := false
   116  	for _, a := range arg {
   117  		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
   118  			foundIt = true
   119  		}
   120  	}
   121  	if !foundIt {
   122  		args = append(args, "--debug")
   123  	}
   124  
   125  	if d.storageDriver != "" {
   126  		args = append(args, "--storage-driver", d.storageDriver)
   127  	}
   128  	if d.execDriver != "" {
   129  		args = append(args, "--exec-driver", d.execDriver)
   130  	}
   131  
   132  	args = append(args, arg...)
   133  	d.cmd = exec.Command(dockerBinary, args...)
   134  
   135  	d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
   136  	if err != nil {
   137  		d.c.Fatalf("[%s] Could not create %s/docker.log: %v", d.id, d.folder, err)
   138  	}
   139  
   140  	d.cmd.Stdout = d.logFile
   141  	d.cmd.Stderr = d.logFile
   142  
   143  	if err := d.cmd.Start(); err != nil {
   144  		return fmt.Errorf("[%s] could not start daemon container: %v", d.id, err)
   145  	}
   146  
   147  	wait := make(chan error)
   148  
   149  	go func() {
   150  		wait <- d.cmd.Wait()
   151  		d.c.Logf("[%s] exiting daemon", d.id)
   152  		close(wait)
   153  	}()
   154  
   155  	d.wait = wait
   156  
   157  	tick := time.Tick(500 * time.Millisecond)
   158  	// make sure daemon is ready to receive requests
   159  	startTime := time.Now().Unix()
   160  	for {
   161  		d.c.Logf("[%s] waiting for daemon to start", d.id)
   162  		if time.Now().Unix()-startTime > 5 {
   163  			// After 5 seconds, give up
   164  			return fmt.Errorf("[%s] Daemon exited and never started", d.id)
   165  		}
   166  		select {
   167  		case <-time.After(2 * time.Second):
   168  			return fmt.Errorf("[%s] timeout: daemon does not respond", d.id)
   169  		case <-tick:
   170  			c, err := net.Dial("unix", filepath.Join(d.folder, "docker.sock"))
   171  			if err != nil {
   172  				continue
   173  			}
   174  
   175  			client := httputil.NewClientConn(c, nil)
   176  			defer client.Close()
   177  
   178  			req, err := http.NewRequest("GET", "/_ping", nil)
   179  			if err != nil {
   180  				d.c.Fatalf("[%s] could not create new request: %v", d.id, err)
   181  			}
   182  
   183  			resp, err := client.Do(req)
   184  			if err != nil {
   185  				continue
   186  			}
   187  			if resp.StatusCode != http.StatusOK {
   188  				d.c.Logf("[%s] received status != 200 OK: %s", d.id, resp.Status)
   189  			}
   190  
   191  			d.c.Logf("[%s] daemon started", d.id)
   192  			return nil
   193  		}
   194  	}
   195  }
   196  
   197  // StartWithBusybox will first start the daemon with Daemon.Start()
   198  // then save the busybox image from the main daemon and load it into this Daemon instance.
   199  func (d *Daemon) StartWithBusybox(arg ...string) error {
   200  	if err := d.Start(arg...); err != nil {
   201  		return err
   202  	}
   203  	bb := filepath.Join(d.folder, "busybox.tar")
   204  	if _, err := os.Stat(bb); err != nil {
   205  		if !os.IsNotExist(err) {
   206  			return fmt.Errorf("unexpected error on busybox.tar stat: %v", err)
   207  		}
   208  		// saving busybox image from main daemon
   209  		if err := exec.Command(dockerBinary, "save", "--output", bb, "busybox:latest").Run(); err != nil {
   210  			return fmt.Errorf("could not save busybox image: %v", err)
   211  		}
   212  	}
   213  	// loading busybox image to this daemon
   214  	if _, err := d.Cmd("load", "--input", bb); err != nil {
   215  		return fmt.Errorf("could not load busybox image: %v", err)
   216  	}
   217  	if err := os.Remove(bb); err != nil {
   218  		d.c.Logf("Could not remove %s: %v", bb, err)
   219  	}
   220  	return nil
   221  }
   222  
   223  // Stop will send a SIGINT every second and wait for the daemon to stop.
   224  // If it timeouts, a SIGKILL is sent.
   225  // Stop will not delete the daemon directory. If a purged daemon is needed,
   226  // instantiate a new one with NewDaemon.
   227  func (d *Daemon) Stop() error {
   228  	if d.cmd == nil || d.wait == nil {
   229  		return errors.New("daemon not started")
   230  	}
   231  
   232  	defer func() {
   233  		d.logFile.Close()
   234  		d.cmd = nil
   235  	}()
   236  
   237  	i := 1
   238  	tick := time.Tick(time.Second)
   239  
   240  	if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
   241  		return fmt.Errorf("could not send signal: %v", err)
   242  	}
   243  out1:
   244  	for {
   245  		select {
   246  		case err := <-d.wait:
   247  			return err
   248  		case <-time.After(15 * time.Second):
   249  			// time for stopping jobs and run onShutdown hooks
   250  			d.c.Log("timeout")
   251  			break out1
   252  		}
   253  	}
   254  
   255  out2:
   256  	for {
   257  		select {
   258  		case err := <-d.wait:
   259  			return err
   260  		case <-tick:
   261  			i++
   262  			if i > 4 {
   263  				d.c.Logf("tried to interrupt daemon for %d times, now try to kill it", i)
   264  				break out2
   265  			}
   266  			d.c.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid)
   267  			if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
   268  				return fmt.Errorf("could not send signal: %v", err)
   269  			}
   270  		}
   271  	}
   272  
   273  	if err := d.cmd.Process.Kill(); err != nil {
   274  		d.c.Logf("Could not kill daemon: %v", err)
   275  		return err
   276  	}
   277  
   278  	return nil
   279  }
   280  
   281  // Restart will restart the daemon by first stopping it and then starting it.
   282  func (d *Daemon) Restart(arg ...string) error {
   283  	d.Stop()
   284  	return d.Start(arg...)
   285  }
   286  
   287  func (d *Daemon) sock() string {
   288  	return fmt.Sprintf("unix://%s/docker.sock", d.folder)
   289  }
   290  
   291  // Cmd will execute a docker CLI command against this Daemon.
   292  // Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version
   293  func (d *Daemon) Cmd(name string, arg ...string) (string, error) {
   294  	args := []string{"--host", d.sock(), name}
   295  	args = append(args, arg...)
   296  	c := exec.Command(dockerBinary, args...)
   297  	b, err := c.CombinedOutput()
   298  	return string(b), err
   299  }
   300  
   301  // CmdWithArgs will execute a docker CLI command against a daemon with the
   302  // given additional arguments
   303  func (d *Daemon) CmdWithArgs(daemonArgs []string, name string, arg ...string) (string, error) {
   304  	args := append(daemonArgs, name)
   305  	args = append(args, arg...)
   306  	c := exec.Command(dockerBinary, args...)
   307  	b, err := c.CombinedOutput()
   308  	return string(b), err
   309  }
   310  
   311  // LogfileName returns the path the the daemon's log file
   312  func (d *Daemon) LogfileName() string {
   313  	return d.logFile.Name()
   314  }
   315  
   316  func daemonHost() string {
   317  	daemonURLStr := "unix://" + opts.DefaultUnixSocket
   318  	if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" {
   319  		daemonURLStr = daemonHostVar
   320  	}
   321  	return daemonURLStr
   322  }
   323  
   324  func sockConn(timeout time.Duration) (net.Conn, error) {
   325  	daemon := daemonHost()
   326  	daemonURL, err := url.Parse(daemon)
   327  	if err != nil {
   328  		return nil, fmt.Errorf("could not parse url %q: %v", daemon, err)
   329  	}
   330  
   331  	var c net.Conn
   332  	switch daemonURL.Scheme {
   333  	case "unix":
   334  		return net.DialTimeout(daemonURL.Scheme, daemonURL.Path, timeout)
   335  	case "tcp":
   336  		return net.DialTimeout(daemonURL.Scheme, daemonURL.Host, timeout)
   337  	default:
   338  		return c, fmt.Errorf("unknown scheme %v (%s)", daemonURL.Scheme, daemon)
   339  	}
   340  }
   341  
   342  func sockRequest(method, endpoint string, data interface{}) (int, []byte, error) {
   343  	jsonData := bytes.NewBuffer(nil)
   344  	if err := json.NewEncoder(jsonData).Encode(data); err != nil {
   345  		return -1, nil, err
   346  	}
   347  
   348  	res, body, err := sockRequestRaw(method, endpoint, jsonData, "application/json")
   349  	if err != nil {
   350  		return -1, nil, err
   351  	}
   352  	b, err := readBody(body)
   353  	return res.StatusCode, b, err
   354  }
   355  
   356  func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) {
   357  	c, err := sockConn(time.Duration(10 * time.Second))
   358  	if err != nil {
   359  		return nil, nil, fmt.Errorf("could not dial docker daemon: %v", err)
   360  	}
   361  
   362  	client := httputil.NewClientConn(c, nil)
   363  
   364  	req, err := http.NewRequest(method, endpoint, data)
   365  	if err != nil {
   366  		client.Close()
   367  		return nil, nil, fmt.Errorf("could not create new request: %v", err)
   368  	}
   369  
   370  	if ct != "" {
   371  		req.Header.Set("Content-Type", ct)
   372  	}
   373  
   374  	resp, err := client.Do(req)
   375  	if err != nil {
   376  		client.Close()
   377  		return nil, nil, fmt.Errorf("could not perform request: %v", err)
   378  	}
   379  	body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
   380  		defer resp.Body.Close()
   381  		return client.Close()
   382  	})
   383  
   384  	return resp, body, nil
   385  }
   386  
   387  func readBody(b io.ReadCloser) ([]byte, error) {
   388  	defer b.Close()
   389  	return ioutil.ReadAll(b)
   390  }
   391  
   392  func deleteContainer(container string) error {
   393  	container = strings.TrimSpace(strings.Replace(container, "\n", " ", -1))
   394  	rmArgs := strings.Split(fmt.Sprintf("rm -fv %v", container), " ")
   395  	exitCode, err := runCommand(exec.Command(dockerBinary, rmArgs...))
   396  	// set error manually if not set
   397  	if exitCode != 0 && err == nil {
   398  		err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero")
   399  	}
   400  
   401  	return err
   402  }
   403  
   404  func getAllContainers() (string, error) {
   405  	getContainersCmd := exec.Command(dockerBinary, "ps", "-q", "-a")
   406  	out, exitCode, err := runCommandWithOutput(getContainersCmd)
   407  	if exitCode != 0 && err == nil {
   408  		err = fmt.Errorf("failed to get a list of containers: %v\n", out)
   409  	}
   410  
   411  	return out, err
   412  }
   413  
   414  func deleteAllContainers() error {
   415  	containers, err := getAllContainers()
   416  	if err != nil {
   417  		fmt.Println(containers)
   418  		return err
   419  	}
   420  
   421  	if err = deleteContainer(containers); err != nil {
   422  		return err
   423  	}
   424  	return nil
   425  }
   426  
   427  var protectedImages = map[string]struct{}{}
   428  
   429  func init() {
   430  	out, err := exec.Command(dockerBinary, "images").CombinedOutput()
   431  	if err != nil {
   432  		panic(err)
   433  	}
   434  	lines := strings.Split(string(out), "\n")[1:]
   435  	for _, l := range lines {
   436  		if l == "" {
   437  			continue
   438  		}
   439  		fields := strings.Fields(l)
   440  		imgTag := fields[0] + ":" + fields[1]
   441  		// just for case if we have dangling images in tested daemon
   442  		if imgTag != "<none>:<none>" {
   443  			protectedImages[imgTag] = struct{}{}
   444  		}
   445  	}
   446  }
   447  
   448  func deleteAllImages() error {
   449  	out, err := exec.Command(dockerBinary, "images").CombinedOutput()
   450  	if err != nil {
   451  		return err
   452  	}
   453  	lines := strings.Split(string(out), "\n")[1:]
   454  	var imgs []string
   455  	for _, l := range lines {
   456  		if l == "" {
   457  			continue
   458  		}
   459  		fields := strings.Fields(l)
   460  		imgTag := fields[0] + ":" + fields[1]
   461  		if _, ok := protectedImages[imgTag]; !ok {
   462  			if fields[0] == "<none>" {
   463  				imgs = append(imgs, fields[2])
   464  				continue
   465  			}
   466  			imgs = append(imgs, imgTag)
   467  		}
   468  	}
   469  	if len(imgs) == 0 {
   470  		return nil
   471  	}
   472  	args := append([]string{"rmi", "-f"}, imgs...)
   473  	if err := exec.Command(dockerBinary, args...).Run(); err != nil {
   474  		return err
   475  	}
   476  	return nil
   477  }
   478  
   479  func getPausedContainers() (string, error) {
   480  	getPausedContainersCmd := exec.Command(dockerBinary, "ps", "-f", "status=paused", "-q", "-a")
   481  	out, exitCode, err := runCommandWithOutput(getPausedContainersCmd)
   482  	if exitCode != 0 && err == nil {
   483  		err = fmt.Errorf("failed to get a list of paused containers: %v\n", out)
   484  	}
   485  
   486  	return out, err
   487  }
   488  
   489  func getSliceOfPausedContainers() ([]string, error) {
   490  	out, err := getPausedContainers()
   491  	if err == nil {
   492  		if len(out) == 0 {
   493  			return nil, err
   494  		}
   495  		slice := strings.Split(strings.TrimSpace(out), "\n")
   496  		return slice, err
   497  	}
   498  	return []string{out}, err
   499  }
   500  
   501  func unpauseContainer(container string) error {
   502  	unpauseCmd := exec.Command(dockerBinary, "unpause", container)
   503  	exitCode, err := runCommand(unpauseCmd)
   504  	if exitCode != 0 && err == nil {
   505  		err = fmt.Errorf("failed to unpause container")
   506  	}
   507  
   508  	return nil
   509  }
   510  
   511  func unpauseAllContainers() error {
   512  	containers, err := getPausedContainers()
   513  	if err != nil {
   514  		fmt.Println(containers)
   515  		return err
   516  	}
   517  
   518  	containers = strings.Replace(containers, "\n", " ", -1)
   519  	containers = strings.Trim(containers, " ")
   520  	containerList := strings.Split(containers, " ")
   521  
   522  	for _, value := range containerList {
   523  		if err = unpauseContainer(value); err != nil {
   524  			return err
   525  		}
   526  	}
   527  
   528  	return nil
   529  }
   530  
   531  func deleteImages(images ...string) error {
   532  	args := []string{"rmi", "-f"}
   533  	args = append(args, images...)
   534  	rmiCmd := exec.Command(dockerBinary, args...)
   535  	exitCode, err := runCommand(rmiCmd)
   536  	// set error manually if not set
   537  	if exitCode != 0 && err == nil {
   538  		err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero")
   539  	}
   540  	return err
   541  }
   542  
   543  func imageExists(image string) error {
   544  	inspectCmd := exec.Command(dockerBinary, "inspect", image)
   545  	exitCode, err := runCommand(inspectCmd)
   546  	if exitCode != 0 && err == nil {
   547  		err = fmt.Errorf("couldn't find image %q", image)
   548  	}
   549  	return err
   550  }
   551  
   552  func pullImageIfNotExist(image string) (err error) {
   553  	if err := imageExists(image); err != nil {
   554  		pullCmd := exec.Command(dockerBinary, "pull", image)
   555  		_, exitCode, err := runCommandWithOutput(pullCmd)
   556  
   557  		if err != nil || exitCode != 0 {
   558  			err = fmt.Errorf("image %q wasn't found locally and it couldn't be pulled: %s", image, err)
   559  		}
   560  	}
   561  	return
   562  }
   563  
   564  func dockerCmdWithError(c *check.C, args ...string) (string, int, error) {
   565  	return runCommandWithOutput(exec.Command(dockerBinary, args...))
   566  }
   567  
   568  func dockerCmdWithStdoutStderr(c *check.C, args ...string) (string, string, int) {
   569  	stdout, stderr, status, err := runCommandWithStdoutStderr(exec.Command(dockerBinary, args...))
   570  	c.Assert(err, check.IsNil, check.Commentf("%q failed with errors: %s, %v", strings.Join(args, " "), stderr, err))
   571  	return stdout, stderr, status
   572  }
   573  
   574  func dockerCmd(c *check.C, args ...string) (string, int) {
   575  	out, status, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
   576  	c.Assert(err, check.IsNil, check.Commentf("%q failed with errors: %s, %v", strings.Join(args, " "), out, err))
   577  	return out, status
   578  }
   579  
   580  // execute a docker command with a timeout
   581  func dockerCmdWithTimeout(timeout time.Duration, args ...string) (string, int, error) {
   582  	out, status, err := runCommandWithOutputAndTimeout(exec.Command(dockerBinary, args...), timeout)
   583  	if err != nil {
   584  		return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out)
   585  	}
   586  	return out, status, err
   587  }
   588  
   589  // execute a docker command in a directory
   590  func dockerCmdInDir(c *check.C, path string, args ...string) (string, int, error) {
   591  	dockerCommand := exec.Command(dockerBinary, args...)
   592  	dockerCommand.Dir = path
   593  	out, status, err := runCommandWithOutput(dockerCommand)
   594  	if err != nil {
   595  		return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out)
   596  	}
   597  	return out, status, err
   598  }
   599  
   600  // execute a docker command in a directory with a timeout
   601  func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...string) (string, int, error) {
   602  	dockerCommand := exec.Command(dockerBinary, args...)
   603  	dockerCommand.Dir = path
   604  	out, status, err := runCommandWithOutputAndTimeout(dockerCommand, timeout)
   605  	if err != nil {
   606  		return out, status, fmt.Errorf("%q failed with errors: %v : %q)", strings.Join(args, " "), err, out)
   607  	}
   608  	return out, status, err
   609  }
   610  
   611  func findContainerIP(c *check.C, id string, vargs ...string) string {
   612  	args := append(vargs, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id)
   613  	cmd := exec.Command(dockerBinary, args...)
   614  	out, _, err := runCommandWithOutput(cmd)
   615  	if err != nil {
   616  		c.Fatal(err, out)
   617  	}
   618  
   619  	return strings.Trim(out, " \r\n'")
   620  }
   621  
   622  func (d *Daemon) findContainerIP(id string) string {
   623  	return findContainerIP(d.c, id, "--host", d.sock())
   624  }
   625  
   626  func getContainerCount() (int, error) {
   627  	const containers = "Containers:"
   628  
   629  	cmd := exec.Command(dockerBinary, "info")
   630  	out, _, err := runCommandWithOutput(cmd)
   631  	if err != nil {
   632  		return 0, err
   633  	}
   634  
   635  	lines := strings.Split(out, "\n")
   636  	for _, line := range lines {
   637  		if strings.Contains(line, containers) {
   638  			output := strings.TrimSpace(line)
   639  			output = strings.TrimLeft(output, containers)
   640  			output = strings.Trim(output, " ")
   641  			containerCount, err := strconv.Atoi(output)
   642  			if err != nil {
   643  				return 0, err
   644  			}
   645  			return containerCount, nil
   646  		}
   647  	}
   648  	return 0, fmt.Errorf("couldn't find the Container count in the output")
   649  }
   650  
   651  // FakeContext creates directories that can be used as a build context
   652  type FakeContext struct {
   653  	Dir string
   654  }
   655  
   656  // Add a file at a path, creating directories where necessary
   657  func (f *FakeContext) Add(file, content string) error {
   658  	return f.addFile(file, []byte(content))
   659  }
   660  
   661  func (f *FakeContext) addFile(file string, content []byte) error {
   662  	filepath := path.Join(f.Dir, file)
   663  	dirpath := path.Dir(filepath)
   664  	if dirpath != "." {
   665  		if err := os.MkdirAll(dirpath, 0755); err != nil {
   666  			return err
   667  		}
   668  	}
   669  	return ioutil.WriteFile(filepath, content, 0644)
   670  
   671  }
   672  
   673  // Delete a file at a path
   674  func (f *FakeContext) Delete(file string) error {
   675  	filepath := path.Join(f.Dir, file)
   676  	return os.RemoveAll(filepath)
   677  }
   678  
   679  // Close deletes the context
   680  func (f *FakeContext) Close() error {
   681  	return os.RemoveAll(f.Dir)
   682  }
   683  
   684  func fakeContextFromNewTempDir() (*FakeContext, error) {
   685  	tmp, err := ioutil.TempDir("", "fake-context")
   686  	if err != nil {
   687  		return nil, err
   688  	}
   689  	if err := os.Chmod(tmp, 0755); err != nil {
   690  		return nil, err
   691  	}
   692  	return fakeContextFromDir(tmp), nil
   693  }
   694  
   695  func fakeContextFromDir(dir string) *FakeContext {
   696  	return &FakeContext{dir}
   697  }
   698  
   699  func fakeContextWithFiles(files map[string]string) (*FakeContext, error) {
   700  	ctx, err := fakeContextFromNewTempDir()
   701  	if err != nil {
   702  		return nil, err
   703  	}
   704  	for file, content := range files {
   705  		if err := ctx.Add(file, content); err != nil {
   706  			ctx.Close()
   707  			return nil, err
   708  		}
   709  	}
   710  	return ctx, nil
   711  }
   712  
   713  func fakeContextAddDockerfile(ctx *FakeContext, dockerfile string) error {
   714  	if err := ctx.Add("Dockerfile", dockerfile); err != nil {
   715  		ctx.Close()
   716  		return err
   717  	}
   718  	return nil
   719  }
   720  
   721  func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) {
   722  	ctx, err := fakeContextWithFiles(files)
   723  	if err != nil {
   724  		return nil, err
   725  	}
   726  	if err := fakeContextAddDockerfile(ctx, dockerfile); err != nil {
   727  		return nil, err
   728  	}
   729  	return ctx, nil
   730  }
   731  
   732  // FakeStorage is a static file server. It might be running locally or remotely
   733  // on test host.
   734  type FakeStorage interface {
   735  	Close() error
   736  	URL() string
   737  	CtxDir() string
   738  }
   739  
   740  func fakeBinaryStorage(archives map[string]*bytes.Buffer) (FakeStorage, error) {
   741  	ctx, err := fakeContextFromNewTempDir()
   742  	if err != nil {
   743  		return nil, err
   744  	}
   745  	for name, content := range archives {
   746  		if err := ctx.addFile(name, content.Bytes()); err != nil {
   747  			return nil, err
   748  		}
   749  	}
   750  	return fakeStorageWithContext(ctx)
   751  }
   752  
   753  // fakeStorage returns either a local or remote (at daemon machine) file server
   754  func fakeStorage(files map[string]string) (FakeStorage, error) {
   755  	ctx, err := fakeContextWithFiles(files)
   756  	if err != nil {
   757  		return nil, err
   758  	}
   759  	return fakeStorageWithContext(ctx)
   760  }
   761  
   762  // fakeStorageWithContext returns either a local or remote (at daemon machine) file server
   763  func fakeStorageWithContext(ctx *FakeContext) (FakeStorage, error) {
   764  	if isLocalDaemon {
   765  		return newLocalFakeStorage(ctx)
   766  	}
   767  	return newRemoteFileServer(ctx)
   768  }
   769  
   770  // localFileStorage is a file storage on the running machine
   771  type localFileStorage struct {
   772  	*FakeContext
   773  	*httptest.Server
   774  }
   775  
   776  func (s *localFileStorage) URL() string {
   777  	return s.Server.URL
   778  }
   779  
   780  func (s *localFileStorage) CtxDir() string {
   781  	return s.FakeContext.Dir
   782  }
   783  
   784  func (s *localFileStorage) Close() error {
   785  	defer s.Server.Close()
   786  	return s.FakeContext.Close()
   787  }
   788  
   789  func newLocalFakeStorage(ctx *FakeContext) (*localFileStorage, error) {
   790  	handler := http.FileServer(http.Dir(ctx.Dir))
   791  	server := httptest.NewServer(handler)
   792  	return &localFileStorage{
   793  		FakeContext: ctx,
   794  		Server:      server,
   795  	}, nil
   796  }
   797  
   798  // remoteFileServer is a containerized static file server started on the remote
   799  // testing machine to be used in URL-accepting docker build functionality.
   800  type remoteFileServer struct {
   801  	host      string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712
   802  	container string
   803  	image     string
   804  	ctx       *FakeContext
   805  }
   806  
   807  func (f *remoteFileServer) URL() string {
   808  	u := url.URL{
   809  		Scheme: "http",
   810  		Host:   f.host}
   811  	return u.String()
   812  }
   813  
   814  func (f *remoteFileServer) CtxDir() string {
   815  	return f.ctx.Dir
   816  }
   817  
   818  func (f *remoteFileServer) Close() error {
   819  	defer func() {
   820  		if f.ctx != nil {
   821  			f.ctx.Close()
   822  		}
   823  		if f.image != "" {
   824  			deleteImages(f.image)
   825  		}
   826  	}()
   827  	if f.container == "" {
   828  		return nil
   829  	}
   830  	return deleteContainer(f.container)
   831  }
   832  
   833  func newRemoteFileServer(ctx *FakeContext) (*remoteFileServer, error) {
   834  	var (
   835  		image     = fmt.Sprintf("fileserver-img-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
   836  		container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
   837  	)
   838  
   839  	// Build the image
   840  	if err := fakeContextAddDockerfile(ctx, `FROM httpserver
   841  COPY . /static`); err != nil {
   842  		return nil, fmt.Errorf("Cannot add Dockerfile to context: %v", err)
   843  	}
   844  	if _, err := buildImageFromContext(image, ctx, false); err != nil {
   845  		return nil, fmt.Errorf("failed building file storage container image: %v", err)
   846  	}
   847  
   848  	// Start the container
   849  	runCmd := exec.Command(dockerBinary, "run", "-d", "-P", "--name", container, image)
   850  	if out, ec, err := runCommandWithOutput(runCmd); err != nil {
   851  		return nil, fmt.Errorf("failed to start file storage container. ec=%v\nout=%s\nerr=%v", ec, out, err)
   852  	}
   853  
   854  	// Find out the system assigned port
   855  	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "port", container, "80/tcp"))
   856  	if err != nil {
   857  		return nil, fmt.Errorf("failed to find container port: err=%v\nout=%s", err, out)
   858  	}
   859  
   860  	return &remoteFileServer{
   861  		container: container,
   862  		image:     image,
   863  		host:      strings.Trim(out, "\n"),
   864  		ctx:       ctx}, nil
   865  }
   866  
   867  func inspectFieldAndMarshall(name, field string, output interface{}) error {
   868  	str, err := inspectFieldJSON(name, field)
   869  	if err != nil {
   870  		return err
   871  	}
   872  
   873  	return json.Unmarshal([]byte(str), output)
   874  }
   875  
   876  func inspectFilter(name, filter string) (string, error) {
   877  	format := fmt.Sprintf("{{%s}}", filter)
   878  	inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name)
   879  	out, exitCode, err := runCommandWithOutput(inspectCmd)
   880  	if err != nil || exitCode != 0 {
   881  		return "", fmt.Errorf("failed to inspect container %s: %s", name, out)
   882  	}
   883  	return strings.TrimSpace(out), nil
   884  }
   885  
   886  func inspectField(name, field string) (string, error) {
   887  	return inspectFilter(name, fmt.Sprintf(".%s", field))
   888  }
   889  
   890  func inspectFieldJSON(name, field string) (string, error) {
   891  	return inspectFilter(name, fmt.Sprintf("json .%s", field))
   892  }
   893  
   894  func inspectFieldMap(name, path, field string) (string, error) {
   895  	return inspectFilter(name, fmt.Sprintf("index .%s %q", path, field))
   896  }
   897  
   898  func inspectMountSourceField(name, destination string) (string, error) {
   899  	m, err := inspectMountPoint(name, destination)
   900  	if err != nil {
   901  		return "", err
   902  	}
   903  	return m.Source, nil
   904  }
   905  
   906  func inspectMountPoint(name, destination string) (types.MountPoint, error) {
   907  	out, err := inspectFieldJSON(name, "Mounts")
   908  	if err != nil {
   909  		return types.MountPoint{}, err
   910  	}
   911  
   912  	return inspectMountPointJSON(out, destination)
   913  }
   914  
   915  var errMountNotFound = errors.New("mount point not found")
   916  
   917  func inspectMountPointJSON(j, destination string) (types.MountPoint, error) {
   918  	var mp []types.MountPoint
   919  	if err := unmarshalJSON([]byte(j), &mp); err != nil {
   920  		return types.MountPoint{}, err
   921  	}
   922  
   923  	var m *types.MountPoint
   924  	for _, c := range mp {
   925  		if c.Destination == destination {
   926  			m = &c
   927  			break
   928  		}
   929  	}
   930  
   931  	if m == nil {
   932  		return types.MountPoint{}, errMountNotFound
   933  	}
   934  
   935  	return *m, nil
   936  }
   937  
   938  func getIDByName(name string) (string, error) {
   939  	return inspectField(name, "Id")
   940  }
   941  
   942  // getContainerState returns the exit code of the container
   943  // and true if it's running
   944  // the exit code should be ignored if it's running
   945  func getContainerState(c *check.C, id string) (int, bool, error) {
   946  	var (
   947  		exitStatus int
   948  		running    bool
   949  	)
   950  	out, exitCode := dockerCmd(c, "inspect", "--format={{.State.Running}} {{.State.ExitCode}}", id)
   951  	if exitCode != 0 {
   952  		return 0, false, fmt.Errorf("%q doesn't exist: %s", id, out)
   953  	}
   954  
   955  	out = strings.Trim(out, "\n")
   956  	splitOutput := strings.Split(out, " ")
   957  	if len(splitOutput) != 2 {
   958  		return 0, false, fmt.Errorf("failed to get container state: output is broken")
   959  	}
   960  	if splitOutput[0] == "true" {
   961  		running = true
   962  	}
   963  	if n, err := strconv.Atoi(splitOutput[1]); err == nil {
   964  		exitStatus = n
   965  	} else {
   966  		return 0, false, fmt.Errorf("failed to get container state: couldn't parse integer")
   967  	}
   968  
   969  	return exitStatus, running, nil
   970  }
   971  
   972  func buildImageCmd(name, dockerfile string, useCache bool) *exec.Cmd {
   973  	args := []string{"-D", "build", "-t", name}
   974  	if !useCache {
   975  		args = append(args, "--no-cache")
   976  	}
   977  	args = append(args, "-")
   978  	buildCmd := exec.Command(dockerBinary, args...)
   979  	buildCmd.Stdin = strings.NewReader(dockerfile)
   980  	return buildCmd
   981  
   982  }
   983  
   984  func buildImageWithOut(name, dockerfile string, useCache bool) (string, string, error) {
   985  	buildCmd := buildImageCmd(name, dockerfile, useCache)
   986  	out, exitCode, err := runCommandWithOutput(buildCmd)
   987  	if err != nil || exitCode != 0 {
   988  		return "", out, fmt.Errorf("failed to build the image: %s", out)
   989  	}
   990  	id, err := getIDByName(name)
   991  	if err != nil {
   992  		return "", out, err
   993  	}
   994  	return id, out, nil
   995  }
   996  
   997  func buildImageWithStdoutStderr(name, dockerfile string, useCache bool) (string, string, string, error) {
   998  	buildCmd := buildImageCmd(name, dockerfile, useCache)
   999  	stdout, stderr, exitCode, err := runCommandWithStdoutStderr(buildCmd)
  1000  	if err != nil || exitCode != 0 {
  1001  		return "", stdout, stderr, fmt.Errorf("failed to build the image: %s", stdout)
  1002  	}
  1003  	id, err := getIDByName(name)
  1004  	if err != nil {
  1005  		return "", stdout, stderr, err
  1006  	}
  1007  	return id, stdout, stderr, nil
  1008  }
  1009  
  1010  func buildImage(name, dockerfile string, useCache bool) (string, error) {
  1011  	id, _, err := buildImageWithOut(name, dockerfile, useCache)
  1012  	return id, err
  1013  }
  1014  
  1015  func buildImageFromContext(name string, ctx *FakeContext, useCache bool) (string, error) {
  1016  	args := []string{"build", "-t", name}
  1017  	if !useCache {
  1018  		args = append(args, "--no-cache")
  1019  	}
  1020  	args = append(args, ".")
  1021  	buildCmd := exec.Command(dockerBinary, args...)
  1022  	buildCmd.Dir = ctx.Dir
  1023  	out, exitCode, err := runCommandWithOutput(buildCmd)
  1024  	if err != nil || exitCode != 0 {
  1025  		return "", fmt.Errorf("failed to build the image: %s", out)
  1026  	}
  1027  	return getIDByName(name)
  1028  }
  1029  
  1030  func buildImageFromPath(name, path string, useCache bool) (string, error) {
  1031  	args := []string{"build", "-t", name}
  1032  	if !useCache {
  1033  		args = append(args, "--no-cache")
  1034  	}
  1035  	args = append(args, path)
  1036  	buildCmd := exec.Command(dockerBinary, args...)
  1037  	out, exitCode, err := runCommandWithOutput(buildCmd)
  1038  	if err != nil || exitCode != 0 {
  1039  		return "", fmt.Errorf("failed to build the image: %s", out)
  1040  	}
  1041  	return getIDByName(name)
  1042  }
  1043  
  1044  type gitServer interface {
  1045  	URL() string
  1046  	Close() error
  1047  }
  1048  
  1049  type localGitServer struct {
  1050  	*httptest.Server
  1051  }
  1052  
  1053  func (r *localGitServer) Close() error {
  1054  	r.Server.Close()
  1055  	return nil
  1056  }
  1057  
  1058  func (r *localGitServer) URL() string {
  1059  	return r.Server.URL
  1060  }
  1061  
  1062  type fakeGit struct {
  1063  	root    string
  1064  	server  gitServer
  1065  	RepoURL string
  1066  }
  1067  
  1068  func (g *fakeGit) Close() {
  1069  	g.server.Close()
  1070  	os.RemoveAll(g.root)
  1071  }
  1072  
  1073  func newFakeGit(name string, files map[string]string, enforceLocalServer bool) (*fakeGit, error) {
  1074  	ctx, err := fakeContextWithFiles(files)
  1075  	if err != nil {
  1076  		return nil, err
  1077  	}
  1078  	defer ctx.Close()
  1079  	curdir, err := os.Getwd()
  1080  	if err != nil {
  1081  		return nil, err
  1082  	}
  1083  	defer os.Chdir(curdir)
  1084  
  1085  	if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil {
  1086  		return nil, fmt.Errorf("error trying to init repo: %s (%s)", err, output)
  1087  	}
  1088  	err = os.Chdir(ctx.Dir)
  1089  	if err != nil {
  1090  		return nil, err
  1091  	}
  1092  	if output, err := exec.Command("git", "config", "user.name", "Fake User").CombinedOutput(); err != nil {
  1093  		return nil, fmt.Errorf("error trying to set 'user.name': %s (%s)", err, output)
  1094  	}
  1095  	if output, err := exec.Command("git", "config", "user.email", "fake.user@example.com").CombinedOutput(); err != nil {
  1096  		return nil, fmt.Errorf("error trying to set 'user.email': %s (%s)", err, output)
  1097  	}
  1098  	if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil {
  1099  		return nil, fmt.Errorf("error trying to add files to repo: %s (%s)", err, output)
  1100  	}
  1101  	if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil {
  1102  		return nil, fmt.Errorf("error trying to commit to repo: %s (%s)", err, output)
  1103  	}
  1104  
  1105  	root, err := ioutil.TempDir("", "docker-test-git-repo")
  1106  	if err != nil {
  1107  		return nil, err
  1108  	}
  1109  	repoPath := filepath.Join(root, name+".git")
  1110  	if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil {
  1111  		os.RemoveAll(root)
  1112  		return nil, fmt.Errorf("error trying to clone --bare: %s (%s)", err, output)
  1113  	}
  1114  	err = os.Chdir(repoPath)
  1115  	if err != nil {
  1116  		os.RemoveAll(root)
  1117  		return nil, err
  1118  	}
  1119  	if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil {
  1120  		os.RemoveAll(root)
  1121  		return nil, fmt.Errorf("error trying to git update-server-info: %s (%s)", err, output)
  1122  	}
  1123  	err = os.Chdir(curdir)
  1124  	if err != nil {
  1125  		os.RemoveAll(root)
  1126  		return nil, err
  1127  	}
  1128  
  1129  	var server gitServer
  1130  	if !enforceLocalServer {
  1131  		// use fakeStorage server, which might be local or remote (at test daemon)
  1132  		server, err = fakeStorageWithContext(fakeContextFromDir(root))
  1133  		if err != nil {
  1134  			return nil, fmt.Errorf("cannot start fake storage: %v", err)
  1135  		}
  1136  	} else {
  1137  		// always start a local http server on CLI test machin
  1138  		httpServer := httptest.NewServer(http.FileServer(http.Dir(root)))
  1139  		server = &localGitServer{httpServer}
  1140  	}
  1141  	return &fakeGit{
  1142  		root:    root,
  1143  		server:  server,
  1144  		RepoURL: fmt.Sprintf("%s/%s.git", server.URL(), name),
  1145  	}, nil
  1146  }
  1147  
  1148  // Write `content` to the file at path `dst`, creating it if necessary,
  1149  // as well as any missing directories.
  1150  // The file is truncated if it already exists.
  1151  // Call c.Fatal() at the first error.
  1152  func writeFile(dst, content string, c *check.C) {
  1153  	// Create subdirectories if necessary
  1154  	if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) {
  1155  		c.Fatal(err)
  1156  	}
  1157  	f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
  1158  	if err != nil {
  1159  		c.Fatal(err)
  1160  	}
  1161  	defer f.Close()
  1162  	// Write content (truncate if it exists)
  1163  	if _, err := io.Copy(f, strings.NewReader(content)); err != nil {
  1164  		c.Fatal(err)
  1165  	}
  1166  }
  1167  
  1168  // Return the contents of file at path `src`.
  1169  // Call c.Fatal() at the first error (including if the file doesn't exist)
  1170  func readFile(src string, c *check.C) (content string) {
  1171  	data, err := ioutil.ReadFile(src)
  1172  	if err != nil {
  1173  		c.Fatal(err)
  1174  	}
  1175  
  1176  	return string(data)
  1177  }
  1178  
  1179  func containerStorageFile(containerID, basename string) string {
  1180  	return filepath.Join("/var/lib/docker/containers", containerID, basename)
  1181  }
  1182  
  1183  // docker commands that use this function must be run with the '-d' switch.
  1184  func runCommandAndReadContainerFile(filename string, cmd *exec.Cmd) ([]byte, error) {
  1185  	out, _, err := runCommandWithOutput(cmd)
  1186  	if err != nil {
  1187  		return nil, fmt.Errorf("%v: %q", err, out)
  1188  	}
  1189  
  1190  	time.Sleep(1 * time.Second)
  1191  
  1192  	contID := strings.TrimSpace(out)
  1193  
  1194  	return readContainerFile(contID, filename)
  1195  }
  1196  
  1197  func readContainerFile(containerID, filename string) ([]byte, error) {
  1198  	f, err := os.Open(containerStorageFile(containerID, filename))
  1199  	if err != nil {
  1200  		return nil, err
  1201  	}
  1202  	defer f.Close()
  1203  
  1204  	content, err := ioutil.ReadAll(f)
  1205  	if err != nil {
  1206  		return nil, err
  1207  	}
  1208  
  1209  	return content, nil
  1210  }
  1211  
  1212  func readContainerFileWithExec(containerID, filename string) ([]byte, error) {
  1213  	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "exec", containerID, "cat", filename))
  1214  	return []byte(out), err
  1215  }
  1216  
  1217  // daemonTime provides the current time on the daemon host
  1218  func daemonTime(c *check.C) time.Time {
  1219  	if isLocalDaemon {
  1220  		return time.Now()
  1221  	}
  1222  
  1223  	status, body, err := sockRequest("GET", "/info", nil)
  1224  	c.Assert(status, check.Equals, http.StatusOK)
  1225  	c.Assert(err, check.IsNil)
  1226  
  1227  	type infoJSON struct {
  1228  		SystemTime string
  1229  	}
  1230  	var info infoJSON
  1231  	if err = json.Unmarshal(body, &info); err != nil {
  1232  		c.Fatalf("unable to unmarshal /info response: %v", err)
  1233  	}
  1234  
  1235  	dt, err := time.Parse(time.RFC3339Nano, info.SystemTime)
  1236  	if err != nil {
  1237  		c.Fatal(err)
  1238  	}
  1239  	return dt
  1240  }
  1241  
  1242  func setupRegistry(c *check.C) *testRegistryV2 {
  1243  	testRequires(c, RegistryHosting)
  1244  	reg, err := newTestRegistryV2(c)
  1245  	if err != nil {
  1246  		c.Fatal(err)
  1247  	}
  1248  
  1249  	// Wait for registry to be ready to serve requests.
  1250  	for i := 0; i != 5; i++ {
  1251  		if err = reg.Ping(); err == nil {
  1252  			break
  1253  		}
  1254  		time.Sleep(100 * time.Millisecond)
  1255  	}
  1256  
  1257  	if err != nil {
  1258  		c.Fatal("Timeout waiting for test registry to become available")
  1259  	}
  1260  	return reg
  1261  }
  1262  
  1263  func setupNotary(c *check.C) *testNotary {
  1264  	testRequires(c, NotaryHosting)
  1265  	ts, err := newTestNotary(c)
  1266  	if err != nil {
  1267  		c.Fatal(err)
  1268  	}
  1269  
  1270  	return ts
  1271  }
  1272  
  1273  // appendBaseEnv appends the minimum set of environment variables to exec the
  1274  // docker cli binary for testing with correct configuration to the given env
  1275  // list.
  1276  func appendBaseEnv(env []string) []string {
  1277  	preserveList := []string{
  1278  		// preserve remote test host
  1279  		"DOCKER_HOST",
  1280  
  1281  		// windows: requires preserving SystemRoot, otherwise dial tcp fails
  1282  		// with "GetAddrInfoW: A non-recoverable error occurred during a database lookup."
  1283  		"SystemRoot",
  1284  	}
  1285  
  1286  	for _, key := range preserveList {
  1287  		if val := os.Getenv(key); val != "" {
  1288  			env = append(env, fmt.Sprintf("%s=%s", key, val))
  1289  		}
  1290  	}
  1291  	return env
  1292  }