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