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