github.com/ctmnz/docker@v1.6.0-rc3/integration-cli/docker_utils.go (about)

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