github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/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  	status, body, err := sockRequestRaw(method, endpoint, jsonData, "application/json")
   317  	if err != nil {
   318  		b, _ := ioutil.ReadAll(body)
   319  		return status, b, err
   320  	}
   321  	var b []byte
   322  	b, err = readBody(body)
   323  	return status, b, err
   324  }
   325  
   326  func sockRequestRaw(method, endpoint string, data io.Reader, ct string) (int, io.ReadCloser, error) {
   327  	c, err := sockConn(time.Duration(10 * time.Second))
   328  	if err != nil {
   329  		return -1, 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 -1, 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 -1, 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.StatusCode, body, err
   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) string {
   574  	cmd := exec.Command(dockerBinary, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id)
   575  	out, _, err := runCommandWithOutput(cmd)
   576  	if err != nil {
   577  		c.Fatal(err, out)
   578  	}
   579  
   580  	return strings.Trim(out, " \r\n'")
   581  }
   582  
   583  func getContainerCount() (int, error) {
   584  	const containers = "Containers:"
   585  
   586  	cmd := exec.Command(dockerBinary, "info")
   587  	out, _, err := runCommandWithOutput(cmd)
   588  	if err != nil {
   589  		return 0, err
   590  	}
   591  
   592  	lines := strings.Split(out, "\n")
   593  	for _, line := range lines {
   594  		if strings.Contains(line, containers) {
   595  			output := strings.TrimSpace(line)
   596  			output = strings.TrimLeft(output, containers)
   597  			output = strings.Trim(output, " ")
   598  			containerCount, err := strconv.Atoi(output)
   599  			if err != nil {
   600  				return 0, err
   601  			}
   602  			return containerCount, nil
   603  		}
   604  	}
   605  	return 0, fmt.Errorf("couldn't find the Container count in the output")
   606  }
   607  
   608  type FakeContext struct {
   609  	Dir string
   610  }
   611  
   612  func (f *FakeContext) Add(file, content string) error {
   613  	filepath := path.Join(f.Dir, file)
   614  	dirpath := path.Dir(filepath)
   615  	if dirpath != "." {
   616  		if err := os.MkdirAll(dirpath, 0755); err != nil {
   617  			return err
   618  		}
   619  	}
   620  	return ioutil.WriteFile(filepath, []byte(content), 0644)
   621  }
   622  
   623  func (f *FakeContext) Delete(file string) error {
   624  	filepath := path.Join(f.Dir, file)
   625  	return os.RemoveAll(filepath)
   626  }
   627  
   628  func (f *FakeContext) Close() error {
   629  	return os.RemoveAll(f.Dir)
   630  }
   631  
   632  func fakeContextFromDir(dir string) *FakeContext {
   633  	return &FakeContext{dir}
   634  }
   635  
   636  func fakeContextWithFiles(files map[string]string) (*FakeContext, error) {
   637  	tmp, err := ioutil.TempDir("", "fake-context")
   638  	if err != nil {
   639  		return nil, err
   640  	}
   641  	if err := os.Chmod(tmp, 0755); err != nil {
   642  		return nil, err
   643  	}
   644  
   645  	ctx := fakeContextFromDir(tmp)
   646  	for file, content := range files {
   647  		if err := ctx.Add(file, content); err != nil {
   648  			ctx.Close()
   649  			return nil, err
   650  		}
   651  	}
   652  	return ctx, nil
   653  }
   654  
   655  func fakeContextAddDockerfile(ctx *FakeContext, dockerfile string) error {
   656  	if err := ctx.Add("Dockerfile", dockerfile); err != nil {
   657  		ctx.Close()
   658  		return err
   659  	}
   660  	return nil
   661  }
   662  
   663  func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) {
   664  	ctx, err := fakeContextWithFiles(files)
   665  	if err != nil {
   666  		ctx.Close()
   667  		return nil, err
   668  	}
   669  	if err := fakeContextAddDockerfile(ctx, dockerfile); err != nil {
   670  		return nil, err
   671  	}
   672  	return ctx, nil
   673  }
   674  
   675  // FakeStorage is a static file server. It might be running locally or remotely
   676  // on test host.
   677  type FakeStorage interface {
   678  	Close() error
   679  	URL() string
   680  	CtxDir() string
   681  }
   682  
   683  // fakeStorage returns either a local or remote (at daemon machine) file server
   684  func fakeStorage(files map[string]string) (FakeStorage, error) {
   685  	ctx, err := fakeContextWithFiles(files)
   686  	if err != nil {
   687  		return nil, err
   688  	}
   689  	return fakeStorageWithContext(ctx)
   690  }
   691  
   692  // fakeStorageWithContext returns either a local or remote (at daemon machine) file server
   693  func fakeStorageWithContext(ctx *FakeContext) (FakeStorage, error) {
   694  	if isLocalDaemon {
   695  		return newLocalFakeStorage(ctx)
   696  	}
   697  	return newRemoteFileServer(ctx)
   698  }
   699  
   700  // localFileStorage is a file storage on the running machine
   701  type localFileStorage struct {
   702  	*FakeContext
   703  	*httptest.Server
   704  }
   705  
   706  func (s *localFileStorage) URL() string {
   707  	return s.Server.URL
   708  }
   709  
   710  func (s *localFileStorage) CtxDir() string {
   711  	return s.FakeContext.Dir
   712  }
   713  
   714  func (s *localFileStorage) Close() error {
   715  	defer s.Server.Close()
   716  	return s.FakeContext.Close()
   717  }
   718  
   719  func newLocalFakeStorage(ctx *FakeContext) (*localFileStorage, error) {
   720  	handler := http.FileServer(http.Dir(ctx.Dir))
   721  	server := httptest.NewServer(handler)
   722  	return &localFileStorage{
   723  		FakeContext: ctx,
   724  		Server:      server,
   725  	}, nil
   726  }
   727  
   728  // remoteFileServer is a containerized static file server started on the remote
   729  // testing machine to be used in URL-accepting docker build functionality.
   730  type remoteFileServer struct {
   731  	host      string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712
   732  	container string
   733  	image     string
   734  	ctx       *FakeContext
   735  }
   736  
   737  func (f *remoteFileServer) URL() string {
   738  	u := url.URL{
   739  		Scheme: "http",
   740  		Host:   f.host}
   741  	return u.String()
   742  }
   743  
   744  func (f *remoteFileServer) CtxDir() string {
   745  	return f.ctx.Dir
   746  }
   747  
   748  func (f *remoteFileServer) Close() error {
   749  	defer func() {
   750  		if f.ctx != nil {
   751  			f.ctx.Close()
   752  		}
   753  		if f.image != "" {
   754  			deleteImages(f.image)
   755  		}
   756  	}()
   757  	if f.container == "" {
   758  		return nil
   759  	}
   760  	return deleteContainer(f.container)
   761  }
   762  
   763  func newRemoteFileServer(ctx *FakeContext) (*remoteFileServer, error) {
   764  	var (
   765  		image     = fmt.Sprintf("fileserver-img-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
   766  		container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10)))
   767  	)
   768  
   769  	// Build the image
   770  	if err := fakeContextAddDockerfile(ctx, `FROM httpserver
   771  COPY . /static`); err != nil {
   772  		return nil, fmt.Errorf("Cannot add Dockerfile to context: %v", err)
   773  	}
   774  	if _, err := buildImageFromContext(image, ctx, false); err != nil {
   775  		return nil, fmt.Errorf("failed building file storage container image: %v", err)
   776  	}
   777  
   778  	// Start the container
   779  	runCmd := exec.Command(dockerBinary, "run", "-d", "-P", "--name", container, image)
   780  	if out, ec, err := runCommandWithOutput(runCmd); err != nil {
   781  		return nil, fmt.Errorf("failed to start file storage container. ec=%v\nout=%s\nerr=%v", ec, out, err)
   782  	}
   783  
   784  	// Find out the system assigned port
   785  	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "port", container, "80/tcp"))
   786  	if err != nil {
   787  		return nil, fmt.Errorf("failed to find container port: err=%v\nout=%s", err, out)
   788  	}
   789  
   790  	return &remoteFileServer{
   791  		container: container,
   792  		image:     image,
   793  		host:      strings.Trim(out, "\n"),
   794  		ctx:       ctx}, nil
   795  }
   796  
   797  func inspectFieldAndMarshall(name, field string, output interface{}) error {
   798  	str, err := inspectFieldJSON(name, field)
   799  	if err != nil {
   800  		return err
   801  	}
   802  
   803  	return json.Unmarshal([]byte(str), output)
   804  }
   805  
   806  func inspectFilter(name, filter string) (string, error) {
   807  	format := fmt.Sprintf("{{%s}}", filter)
   808  	inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name)
   809  	out, exitCode, err := runCommandWithOutput(inspectCmd)
   810  	if err != nil || exitCode != 0 {
   811  		return "", fmt.Errorf("failed to inspect container %s: %s", name, out)
   812  	}
   813  	return strings.TrimSpace(out), nil
   814  }
   815  
   816  func inspectField(name, field string) (string, error) {
   817  	return inspectFilter(name, fmt.Sprintf(".%s", field))
   818  }
   819  
   820  func inspectFieldJSON(name, field string) (string, error) {
   821  	return inspectFilter(name, fmt.Sprintf("json .%s", field))
   822  }
   823  
   824  func inspectFieldMap(name, path, field string) (string, error) {
   825  	return inspectFilter(name, fmt.Sprintf("index .%s %q", path, field))
   826  }
   827  
   828  func getIDByName(name string) (string, error) {
   829  	return inspectField(name, "Id")
   830  }
   831  
   832  // getContainerState returns the exit code of the container
   833  // and true if it's running
   834  // the exit code should be ignored if it's running
   835  func getContainerState(c *check.C, id string) (int, bool, error) {
   836  	var (
   837  		exitStatus int
   838  		running    bool
   839  	)
   840  	out, exitCode := dockerCmd(c, "inspect", "--format={{.State.Running}} {{.State.ExitCode}}", id)
   841  	if exitCode != 0 {
   842  		return 0, false, fmt.Errorf("%q doesn't exist: %s", id, out)
   843  	}
   844  
   845  	out = strings.Trim(out, "\n")
   846  	splitOutput := strings.Split(out, " ")
   847  	if len(splitOutput) != 2 {
   848  		return 0, false, fmt.Errorf("failed to get container state: output is broken")
   849  	}
   850  	if splitOutput[0] == "true" {
   851  		running = true
   852  	}
   853  	if n, err := strconv.Atoi(splitOutput[1]); err == nil {
   854  		exitStatus = n
   855  	} else {
   856  		return 0, false, fmt.Errorf("failed to get container state: couldn't parse integer")
   857  	}
   858  
   859  	return exitStatus, running, nil
   860  }
   861  
   862  func buildImageWithOut(name, dockerfile string, useCache bool) (string, string, error) {
   863  	args := []string{"build", "-t", name}
   864  	if !useCache {
   865  		args = append(args, "--no-cache")
   866  	}
   867  	args = append(args, "-")
   868  	buildCmd := exec.Command(dockerBinary, args...)
   869  	buildCmd.Stdin = strings.NewReader(dockerfile)
   870  	out, exitCode, err := runCommandWithOutput(buildCmd)
   871  	if err != nil || exitCode != 0 {
   872  		return "", out, fmt.Errorf("failed to build the image: %s", out)
   873  	}
   874  	id, err := getIDByName(name)
   875  	if err != nil {
   876  		return "", out, err
   877  	}
   878  	return id, out, nil
   879  }
   880  
   881  func buildImageWithStdoutStderr(name, dockerfile string, useCache bool) (string, string, string, error) {
   882  	args := []string{"build", "-t", name}
   883  	if !useCache {
   884  		args = append(args, "--no-cache")
   885  	}
   886  	args = append(args, "-")
   887  	buildCmd := exec.Command(dockerBinary, args...)
   888  	buildCmd.Stdin = strings.NewReader(dockerfile)
   889  	stdout, stderr, exitCode, err := runCommandWithStdoutStderr(buildCmd)
   890  	if err != nil || exitCode != 0 {
   891  		return "", stdout, stderr, fmt.Errorf("failed to build the image: %s", stdout)
   892  	}
   893  	id, err := getIDByName(name)
   894  	if err != nil {
   895  		return "", stdout, stderr, err
   896  	}
   897  	return id, stdout, stderr, nil
   898  }
   899  
   900  func buildImage(name, dockerfile string, useCache bool) (string, error) {
   901  	id, _, err := buildImageWithOut(name, dockerfile, useCache)
   902  	return id, err
   903  }
   904  
   905  func buildImageFromContext(name string, ctx *FakeContext, useCache bool) (string, error) {
   906  	args := []string{"build", "-t", name}
   907  	if !useCache {
   908  		args = append(args, "--no-cache")
   909  	}
   910  	args = append(args, ".")
   911  	buildCmd := exec.Command(dockerBinary, args...)
   912  	buildCmd.Dir = ctx.Dir
   913  	out, exitCode, err := runCommandWithOutput(buildCmd)
   914  	if err != nil || exitCode != 0 {
   915  		return "", fmt.Errorf("failed to build the image: %s", out)
   916  	}
   917  	return getIDByName(name)
   918  }
   919  
   920  func buildImageFromPath(name, path string, useCache bool) (string, error) {
   921  	args := []string{"build", "-t", name}
   922  	if !useCache {
   923  		args = append(args, "--no-cache")
   924  	}
   925  	args = append(args, path)
   926  	buildCmd := exec.Command(dockerBinary, args...)
   927  	out, exitCode, err := runCommandWithOutput(buildCmd)
   928  	if err != nil || exitCode != 0 {
   929  		return "", fmt.Errorf("failed to build the image: %s", out)
   930  	}
   931  	return getIDByName(name)
   932  }
   933  
   934  type GitServer interface {
   935  	URL() string
   936  	Close() error
   937  }
   938  
   939  type localGitServer struct {
   940  	*httptest.Server
   941  }
   942  
   943  func (r *localGitServer) Close() error {
   944  	r.Server.Close()
   945  	return nil
   946  }
   947  
   948  func (r *localGitServer) URL() string {
   949  	return r.Server.URL
   950  }
   951  
   952  type FakeGIT struct {
   953  	root    string
   954  	server  GitServer
   955  	RepoURL string
   956  }
   957  
   958  func (g *FakeGIT) Close() {
   959  	g.server.Close()
   960  	os.RemoveAll(g.root)
   961  }
   962  
   963  func fakeGIT(name string, files map[string]string, enforceLocalServer bool) (*FakeGIT, error) {
   964  	ctx, err := fakeContextWithFiles(files)
   965  	if err != nil {
   966  		return nil, err
   967  	}
   968  	defer ctx.Close()
   969  	curdir, err := os.Getwd()
   970  	if err != nil {
   971  		return nil, err
   972  	}
   973  	defer os.Chdir(curdir)
   974  
   975  	if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil {
   976  		return nil, fmt.Errorf("error trying to init repo: %s (%s)", err, output)
   977  	}
   978  	err = os.Chdir(ctx.Dir)
   979  	if err != nil {
   980  		return nil, err
   981  	}
   982  	if output, err := exec.Command("git", "config", "user.name", "Fake User").CombinedOutput(); err != nil {
   983  		return nil, fmt.Errorf("error trying to set 'user.name': %s (%s)", err, output)
   984  	}
   985  	if output, err := exec.Command("git", "config", "user.email", "fake.user@example.com").CombinedOutput(); err != nil {
   986  		return nil, fmt.Errorf("error trying to set 'user.email': %s (%s)", err, output)
   987  	}
   988  	if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil {
   989  		return nil, fmt.Errorf("error trying to add files to repo: %s (%s)", err, output)
   990  	}
   991  	if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil {
   992  		return nil, fmt.Errorf("error trying to commit to repo: %s (%s)", err, output)
   993  	}
   994  
   995  	root, err := ioutil.TempDir("", "docker-test-git-repo")
   996  	if err != nil {
   997  		return nil, err
   998  	}
   999  	repoPath := filepath.Join(root, name+".git")
  1000  	if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil {
  1001  		os.RemoveAll(root)
  1002  		return nil, fmt.Errorf("error trying to clone --bare: %s (%s)", err, output)
  1003  	}
  1004  	err = os.Chdir(repoPath)
  1005  	if err != nil {
  1006  		os.RemoveAll(root)
  1007  		return nil, err
  1008  	}
  1009  	if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil {
  1010  		os.RemoveAll(root)
  1011  		return nil, fmt.Errorf("error trying to git update-server-info: %s (%s)", err, output)
  1012  	}
  1013  	err = os.Chdir(curdir)
  1014  	if err != nil {
  1015  		os.RemoveAll(root)
  1016  		return nil, err
  1017  	}
  1018  
  1019  	var server GitServer
  1020  	if !enforceLocalServer {
  1021  		// use fakeStorage server, which might be local or remote (at test daemon)
  1022  		server, err = fakeStorageWithContext(fakeContextFromDir(root))
  1023  		if err != nil {
  1024  			return nil, fmt.Errorf("cannot start fake storage: %v", err)
  1025  		}
  1026  	} else {
  1027  		// always start a local http server on CLI test machin
  1028  		httpServer := httptest.NewServer(http.FileServer(http.Dir(root)))
  1029  		server = &localGitServer{httpServer}
  1030  	}
  1031  	return &FakeGIT{
  1032  		root:    root,
  1033  		server:  server,
  1034  		RepoURL: fmt.Sprintf("%s/%s.git", server.URL(), name),
  1035  	}, nil
  1036  }
  1037  
  1038  // Write `content` to the file at path `dst`, creating it if necessary,
  1039  // as well as any missing directories.
  1040  // The file is truncated if it already exists.
  1041  // Call c.Fatal() at the first error.
  1042  func writeFile(dst, content string, c *check.C) {
  1043  	// Create subdirectories if necessary
  1044  	if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) {
  1045  		c.Fatal(err)
  1046  	}
  1047  	f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
  1048  	if err != nil {
  1049  		c.Fatal(err)
  1050  	}
  1051  	// Write content (truncate if it exists)
  1052  	if _, err := io.Copy(f, strings.NewReader(content)); err != nil {
  1053  		c.Fatal(err)
  1054  	}
  1055  }
  1056  
  1057  // Return the contents of file at path `src`.
  1058  // Call c.Fatal() at the first error (including if the file doesn't exist)
  1059  func readFile(src string, c *check.C) (content string) {
  1060  	data, err := ioutil.ReadFile(src)
  1061  	if err != nil {
  1062  		c.Fatal(err)
  1063  	}
  1064  
  1065  	return string(data)
  1066  }
  1067  
  1068  func containerStorageFile(containerId, basename string) string {
  1069  	return filepath.Join("/var/lib/docker/containers", containerId, basename)
  1070  }
  1071  
  1072  // docker commands that use this function must be run with the '-d' switch.
  1073  func runCommandAndReadContainerFile(filename string, cmd *exec.Cmd) ([]byte, error) {
  1074  	out, _, err := runCommandWithOutput(cmd)
  1075  	if err != nil {
  1076  		return nil, fmt.Errorf("%v: %q", err, out)
  1077  	}
  1078  
  1079  	time.Sleep(1 * time.Second)
  1080  
  1081  	contID := strings.TrimSpace(out)
  1082  
  1083  	return readContainerFile(contID, filename)
  1084  }
  1085  
  1086  func readContainerFile(containerId, filename string) ([]byte, error) {
  1087  	f, err := os.Open(containerStorageFile(containerId, filename))
  1088  	if err != nil {
  1089  		return nil, err
  1090  	}
  1091  	defer f.Close()
  1092  
  1093  	content, err := ioutil.ReadAll(f)
  1094  	if err != nil {
  1095  		return nil, err
  1096  	}
  1097  
  1098  	return content, nil
  1099  }
  1100  
  1101  func readContainerFileWithExec(containerId, filename string) ([]byte, error) {
  1102  	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "exec", containerId, "cat", filename))
  1103  	return []byte(out), err
  1104  }
  1105  
  1106  // daemonTime provides the current time on the daemon host
  1107  func daemonTime(c *check.C) time.Time {
  1108  	if isLocalDaemon {
  1109  		return time.Now()
  1110  	}
  1111  
  1112  	status, body, err := sockRequest("GET", "/info", nil)
  1113  	c.Assert(status, check.Equals, http.StatusOK)
  1114  	c.Assert(err, check.IsNil)
  1115  
  1116  	type infoJSON struct {
  1117  		SystemTime string
  1118  	}
  1119  	var info infoJSON
  1120  	if err = json.Unmarshal(body, &info); err != nil {
  1121  		c.Fatalf("unable to unmarshal /info response: %v", err)
  1122  	}
  1123  
  1124  	dt, err := time.Parse(time.RFC3339Nano, info.SystemTime)
  1125  	if err != nil {
  1126  		c.Fatal(err)
  1127  	}
  1128  	return dt
  1129  }
  1130  
  1131  func setupRegistry(c *check.C) *testRegistryV2 {
  1132  	testRequires(c, RegistryHosting)
  1133  	reg, err := newTestRegistryV2(c)
  1134  	if err != nil {
  1135  		c.Fatal(err)
  1136  	}
  1137  
  1138  	// Wait for registry to be ready to serve requests.
  1139  	for i := 0; i != 5; i++ {
  1140  		if err = reg.Ping(); err == nil {
  1141  			break
  1142  		}
  1143  		time.Sleep(100 * time.Millisecond)
  1144  	}
  1145  
  1146  	if err != nil {
  1147  		c.Fatal("Timeout waiting for test registry to become available")
  1148  	}
  1149  	return reg
  1150  }
  1151  
  1152  // appendBaseEnv appends the minimum set of environment variables to exec the
  1153  // docker cli binary for testing with correct configuration to the given env
  1154  // list.
  1155  func appendBaseEnv(env []string) []string {
  1156  	preserveList := []string{
  1157  		// preserve remote test host
  1158  		"DOCKER_HOST",
  1159  
  1160  		// windows: requires preserving SystemRoot, otherwise dial tcp fails
  1161  		// with "GetAddrInfoW: A non-recoverable error occurred during a database lookup."
  1162  		"SystemRoot",
  1163  	}
  1164  
  1165  	for _, key := range preserveList {
  1166  		if val := os.Getenv(key); val != "" {
  1167  			env = append(env, fmt.Sprintf("%s=%s", key, val))
  1168  		}
  1169  	}
  1170  	return env
  1171  }