github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/integration-cli/daemon/daemon.go (about)

     1  package daemon
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/docker/docker/api"
    18  	"github.com/docker/docker/api/types"
    19  	"github.com/docker/docker/api/types/events"
    20  	"github.com/docker/docker/client"
    21  	"github.com/docker/docker/integration-cli/checker"
    22  	"github.com/docker/docker/integration-cli/request"
    23  	"github.com/docker/docker/opts"
    24  	"github.com/docker/docker/pkg/ioutils"
    25  	"github.com/docker/docker/pkg/stringid"
    26  	"github.com/docker/go-connections/sockets"
    27  	"github.com/docker/go-connections/tlsconfig"
    28  	"github.com/go-check/check"
    29  	"github.com/gotestyourself/gotestyourself/icmd"
    30  	"github.com/pkg/errors"
    31  	"github.com/stretchr/testify/require"
    32  	"golang.org/x/net/context"
    33  )
    34  
    35  type testingT interface {
    36  	require.TestingT
    37  	logT
    38  	Fatalf(string, ...interface{})
    39  }
    40  
    41  type logT interface {
    42  	Logf(string, ...interface{})
    43  }
    44  
    45  // SockRoot holds the path of the default docker integration daemon socket
    46  var SockRoot = filepath.Join(os.TempDir(), "docker-integration")
    47  
    48  var errDaemonNotStarted = errors.New("daemon not started")
    49  
    50  // Daemon represents a Docker daemon for the testing framework.
    51  type Daemon struct {
    52  	GlobalFlags       []string
    53  	Root              string
    54  	Folder            string
    55  	Wait              chan error
    56  	UseDefaultHost    bool
    57  	UseDefaultTLSHost bool
    58  
    59  	id             string
    60  	logFile        *os.File
    61  	stdin          io.WriteCloser
    62  	stdout, stderr io.ReadCloser
    63  	cmd            *exec.Cmd
    64  	storageDriver  string
    65  	userlandProxy  bool
    66  	execRoot       string
    67  	experimental   bool
    68  	dockerBinary   string
    69  	dockerdBinary  string
    70  	log            logT
    71  }
    72  
    73  // Config holds docker daemon integration configuration
    74  type Config struct {
    75  	Experimental bool
    76  }
    77  
    78  type clientConfig struct {
    79  	transport *http.Transport
    80  	scheme    string
    81  	addr      string
    82  }
    83  
    84  // New returns a Daemon instance to be used for testing.
    85  // This will create a directory such as d123456789 in the folder specified by $DOCKER_INTEGRATION_DAEMON_DEST or $DEST.
    86  // The daemon will not automatically start.
    87  func New(t testingT, dockerBinary string, dockerdBinary string, config Config) *Daemon {
    88  	dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
    89  	if dest == "" {
    90  		dest = os.Getenv("DEST")
    91  	}
    92  	if dest == "" {
    93  		t.Fatalf("Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable")
    94  	}
    95  
    96  	if err := os.MkdirAll(SockRoot, 0700); err != nil {
    97  		t.Fatalf("could not create daemon socket root")
    98  	}
    99  
   100  	id := fmt.Sprintf("d%s", stringid.TruncateID(stringid.GenerateRandomID()))
   101  	dir := filepath.Join(dest, id)
   102  	daemonFolder, err := filepath.Abs(dir)
   103  	if err != nil {
   104  		t.Fatalf("Could not make %q an absolute path", dir)
   105  	}
   106  	daemonRoot := filepath.Join(daemonFolder, "root")
   107  
   108  	if err := os.MkdirAll(daemonRoot, 0755); err != nil {
   109  		t.Fatalf("Could not create daemon root %q", dir)
   110  	}
   111  
   112  	userlandProxy := true
   113  	if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" {
   114  		if val, err := strconv.ParseBool(env); err != nil {
   115  			userlandProxy = val
   116  		}
   117  	}
   118  
   119  	return &Daemon{
   120  		id:            id,
   121  		Folder:        daemonFolder,
   122  		Root:          daemonRoot,
   123  		storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"),
   124  		userlandProxy: userlandProxy,
   125  		execRoot:      filepath.Join(os.TempDir(), "docker-execroot", id),
   126  		dockerBinary:  dockerBinary,
   127  		dockerdBinary: dockerdBinary,
   128  		experimental:  config.Experimental,
   129  		log:           t,
   130  	}
   131  }
   132  
   133  // RootDir returns the root directory of the daemon.
   134  func (d *Daemon) RootDir() string {
   135  	return d.Root
   136  }
   137  
   138  // ID returns the generated id of the daemon
   139  func (d *Daemon) ID() string {
   140  	return d.id
   141  }
   142  
   143  // StorageDriver returns the configured storage driver of the daemon
   144  func (d *Daemon) StorageDriver() string {
   145  	return d.storageDriver
   146  }
   147  
   148  // CleanupExecRoot cleans the daemon exec root (network namespaces, ...)
   149  func (d *Daemon) CleanupExecRoot(c *check.C) {
   150  	cleanupExecRoot(c, d.execRoot)
   151  }
   152  
   153  func (d *Daemon) getClientConfig() (*clientConfig, error) {
   154  	var (
   155  		transport *http.Transport
   156  		scheme    string
   157  		addr      string
   158  		proto     string
   159  	)
   160  	if d.UseDefaultTLSHost {
   161  		option := &tlsconfig.Options{
   162  			CAFile:   "fixtures/https/ca.pem",
   163  			CertFile: "fixtures/https/client-cert.pem",
   164  			KeyFile:  "fixtures/https/client-key.pem",
   165  		}
   166  		tlsConfig, err := tlsconfig.Client(*option)
   167  		if err != nil {
   168  			return nil, err
   169  		}
   170  		transport = &http.Transport{
   171  			TLSClientConfig: tlsConfig,
   172  		}
   173  		addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort)
   174  		scheme = "https"
   175  		proto = "tcp"
   176  	} else if d.UseDefaultHost {
   177  		addr = opts.DefaultUnixSocket
   178  		proto = "unix"
   179  		scheme = "http"
   180  		transport = &http.Transport{}
   181  	} else {
   182  		addr = d.sockPath()
   183  		proto = "unix"
   184  		scheme = "http"
   185  		transport = &http.Transport{}
   186  	}
   187  
   188  	if err := sockets.ConfigureTransport(transport, proto, addr); err != nil {
   189  		return nil, err
   190  	}
   191  	transport.DisableKeepAlives = true
   192  
   193  	return &clientConfig{
   194  		transport: transport,
   195  		scheme:    scheme,
   196  		addr:      addr,
   197  	}, nil
   198  }
   199  
   200  // Start starts the daemon and return once it is ready to receive requests.
   201  func (d *Daemon) Start(t testingT, args ...string) {
   202  	if err := d.StartWithError(args...); err != nil {
   203  		t.Fatalf("Error starting daemon with arguments: %v", args)
   204  	}
   205  }
   206  
   207  // StartWithError starts the daemon and return once it is ready to receive requests.
   208  // It returns an error in case it couldn't start.
   209  func (d *Daemon) StartWithError(args ...string) error {
   210  	logFile, err := os.OpenFile(filepath.Join(d.Folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
   211  	if err != nil {
   212  		return errors.Wrapf(err, "[%s] Could not create %s/docker.log", d.id, d.Folder)
   213  	}
   214  
   215  	return d.StartWithLogFile(logFile, args...)
   216  }
   217  
   218  // StartWithLogFile will start the daemon and attach its streams to a given file.
   219  func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
   220  	dockerdBinary, err := exec.LookPath(d.dockerdBinary)
   221  	if err != nil {
   222  		return errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id)
   223  	}
   224  	args := append(d.GlobalFlags,
   225  		"--containerd", "/var/run/docker/containerd/docker-containerd.sock",
   226  		"--data-root", d.Root,
   227  		"--exec-root", d.execRoot,
   228  		"--pidfile", fmt.Sprintf("%s/docker.pid", d.Folder),
   229  		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
   230  	)
   231  	if d.experimental {
   232  		args = append(args, "--experimental", "--init")
   233  	}
   234  	if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
   235  		args = append(args, []string{"--host", d.Sock()}...)
   236  	}
   237  	if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
   238  		args = append(args, []string{"--userns-remap", root}...)
   239  	}
   240  
   241  	// If we don't explicitly set the log-level or debug flag(-D) then
   242  	// turn on debug mode
   243  	foundLog := false
   244  	foundSd := false
   245  	for _, a := range providedArgs {
   246  		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
   247  			foundLog = true
   248  		}
   249  		if strings.Contains(a, "--storage-driver") {
   250  			foundSd = true
   251  		}
   252  	}
   253  	if !foundLog {
   254  		args = append(args, "--debug")
   255  	}
   256  	if d.storageDriver != "" && !foundSd {
   257  		args = append(args, "--storage-driver", d.storageDriver)
   258  	}
   259  
   260  	args = append(args, providedArgs...)
   261  	d.cmd = exec.Command(dockerdBinary, args...)
   262  	d.cmd.Env = append(os.Environ(), "DOCKER_SERVICE_PREFER_OFFLINE_IMAGE=1")
   263  	d.cmd.Stdout = out
   264  	d.cmd.Stderr = out
   265  	d.logFile = out
   266  
   267  	if err := d.cmd.Start(); err != nil {
   268  		return errors.Errorf("[%s] could not start daemon container: %v", d.id, err)
   269  	}
   270  
   271  	wait := make(chan error)
   272  
   273  	go func() {
   274  		wait <- d.cmd.Wait()
   275  		d.log.Logf("[%s] exiting daemon", d.id)
   276  		close(wait)
   277  	}()
   278  
   279  	d.Wait = wait
   280  
   281  	tick := time.Tick(500 * time.Millisecond)
   282  	// make sure daemon is ready to receive requests
   283  	startTime := time.Now().Unix()
   284  	for {
   285  		d.log.Logf("[%s] waiting for daemon to start", d.id)
   286  		if time.Now().Unix()-startTime > 5 {
   287  			// After 5 seconds, give up
   288  			return errors.Errorf("[%s] Daemon exited and never started", d.id)
   289  		}
   290  		select {
   291  		case <-time.After(2 * time.Second):
   292  			return errors.Errorf("[%s] timeout: daemon does not respond", d.id)
   293  		case <-tick:
   294  			clientConfig, err := d.getClientConfig()
   295  			if err != nil {
   296  				return err
   297  			}
   298  
   299  			client := &http.Client{
   300  				Transport: clientConfig.transport,
   301  			}
   302  
   303  			req, err := http.NewRequest("GET", "/_ping", nil)
   304  			if err != nil {
   305  				return errors.Wrapf(err, "[%s] could not create new request", d.id)
   306  			}
   307  			req.URL.Host = clientConfig.addr
   308  			req.URL.Scheme = clientConfig.scheme
   309  			resp, err := client.Do(req)
   310  			if err != nil {
   311  				continue
   312  			}
   313  			resp.Body.Close()
   314  			if resp.StatusCode != http.StatusOK {
   315  				d.log.Logf("[%s] received status != 200 OK: %s\n", d.id, resp.Status)
   316  			}
   317  			d.log.Logf("[%s] daemon started\n", d.id)
   318  			d.Root, err = d.queryRootDir()
   319  			if err != nil {
   320  				return errors.Errorf("[%s] error querying daemon for root directory: %v", d.id, err)
   321  			}
   322  			return nil
   323  		case <-d.Wait:
   324  			return errors.Errorf("[%s] Daemon exited during startup", d.id)
   325  		}
   326  	}
   327  }
   328  
   329  // StartWithBusybox will first start the daemon with Daemon.Start()
   330  // then save the busybox image from the main daemon and load it into this Daemon instance.
   331  func (d *Daemon) StartWithBusybox(t testingT, arg ...string) {
   332  	d.Start(t, arg...)
   333  	d.LoadBusybox(t)
   334  }
   335  
   336  // Kill will send a SIGKILL to the daemon
   337  func (d *Daemon) Kill() error {
   338  	if d.cmd == nil || d.Wait == nil {
   339  		return errDaemonNotStarted
   340  	}
   341  
   342  	defer func() {
   343  		d.logFile.Close()
   344  		d.cmd = nil
   345  	}()
   346  
   347  	if err := d.cmd.Process.Kill(); err != nil {
   348  		return err
   349  	}
   350  
   351  	if err := os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder)); err != nil {
   352  		return err
   353  	}
   354  
   355  	return nil
   356  }
   357  
   358  // Pid returns the pid of the daemon
   359  func (d *Daemon) Pid() int {
   360  	return d.cmd.Process.Pid
   361  }
   362  
   363  // Interrupt stops the daemon by sending it an Interrupt signal
   364  func (d *Daemon) Interrupt() error {
   365  	return d.Signal(os.Interrupt)
   366  }
   367  
   368  // Signal sends the specified signal to the daemon if running
   369  func (d *Daemon) Signal(signal os.Signal) error {
   370  	if d.cmd == nil || d.Wait == nil {
   371  		return errDaemonNotStarted
   372  	}
   373  	return d.cmd.Process.Signal(signal)
   374  }
   375  
   376  // DumpStackAndQuit sends SIGQUIT to the daemon, which triggers it to dump its
   377  // stack to its log file and exit
   378  // This is used primarily for gathering debug information on test timeout
   379  func (d *Daemon) DumpStackAndQuit() {
   380  	if d.cmd == nil || d.cmd.Process == nil {
   381  		return
   382  	}
   383  	SignalDaemonDump(d.cmd.Process.Pid)
   384  }
   385  
   386  // Stop will send a SIGINT every second and wait for the daemon to stop.
   387  // If it times out, a SIGKILL is sent.
   388  // Stop will not delete the daemon directory. If a purged daemon is needed,
   389  // instantiate a new one with NewDaemon.
   390  // If an error occurs while starting the daemon, the test will fail.
   391  func (d *Daemon) Stop(t testingT) {
   392  	err := d.StopWithError()
   393  	if err != nil {
   394  		if err != errDaemonNotStarted {
   395  			t.Fatalf("Error while stopping the daemon %s : %v", d.id, err)
   396  		} else {
   397  			t.Logf("Daemon %s is not started", d.id)
   398  		}
   399  	}
   400  }
   401  
   402  // StopWithError will send a SIGINT every second and wait for the daemon to stop.
   403  // If it timeouts, a SIGKILL is sent.
   404  // Stop will not delete the daemon directory. If a purged daemon is needed,
   405  // instantiate a new one with NewDaemon.
   406  func (d *Daemon) StopWithError() error {
   407  	if d.cmd == nil || d.Wait == nil {
   408  		return errDaemonNotStarted
   409  	}
   410  
   411  	defer func() {
   412  		d.logFile.Close()
   413  		d.cmd = nil
   414  	}()
   415  
   416  	i := 1
   417  	tick := time.Tick(time.Second)
   418  
   419  	if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
   420  		if strings.Contains(err.Error(), "os: process already finished") {
   421  			return errDaemonNotStarted
   422  		}
   423  		return errors.Errorf("could not send signal: %v", err)
   424  	}
   425  out1:
   426  	for {
   427  		select {
   428  		case err := <-d.Wait:
   429  			return err
   430  		case <-time.After(20 * time.Second):
   431  			// time for stopping jobs and run onShutdown hooks
   432  			d.log.Logf("[%s] daemon started", d.id)
   433  			break out1
   434  		}
   435  	}
   436  
   437  out2:
   438  	for {
   439  		select {
   440  		case err := <-d.Wait:
   441  			return err
   442  		case <-tick:
   443  			i++
   444  			if i > 5 {
   445  				d.log.Logf("tried to interrupt daemon for %d times, now try to kill it", i)
   446  				break out2
   447  			}
   448  			d.log.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid)
   449  			if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
   450  				return errors.Errorf("could not send signal: %v", err)
   451  			}
   452  		}
   453  	}
   454  
   455  	if err := d.cmd.Process.Kill(); err != nil {
   456  		d.log.Logf("Could not kill daemon: %v", err)
   457  		return err
   458  	}
   459  
   460  	d.cmd.Wait()
   461  
   462  	if err := os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder)); err != nil {
   463  		return err
   464  	}
   465  
   466  	return nil
   467  }
   468  
   469  // Restart will restart the daemon by first stopping it and the starting it.
   470  // If an error occurs while starting the daemon, the test will fail.
   471  func (d *Daemon) Restart(t testingT, args ...string) {
   472  	d.Stop(t)
   473  	d.handleUserns()
   474  	d.Start(t, args...)
   475  }
   476  
   477  // RestartWithError will restart the daemon by first stopping it and then starting it.
   478  func (d *Daemon) RestartWithError(arg ...string) error {
   479  	if err := d.StopWithError(); err != nil {
   480  		return err
   481  	}
   482  	d.handleUserns()
   483  	return d.StartWithError(arg...)
   484  }
   485  
   486  func (d *Daemon) handleUserns() {
   487  	// in the case of tests running a user namespace-enabled daemon, we have resolved
   488  	// d.Root to be the actual final path of the graph dir after the "uid.gid" of
   489  	// remapped root is added--we need to subtract it from the path before calling
   490  	// start or else we will continue making subdirectories rather than truly restarting
   491  	// with the same location/root:
   492  	if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
   493  		d.Root = filepath.Dir(d.Root)
   494  	}
   495  }
   496  
   497  // LoadBusybox image into the daemon
   498  func (d *Daemon) LoadBusybox(t testingT) {
   499  	clientHost, err := client.NewEnvClient()
   500  	require.NoError(t, err, "failed to create client")
   501  	defer clientHost.Close()
   502  
   503  	ctx := context.Background()
   504  	reader, err := clientHost.ImageSave(ctx, []string{"busybox:latest"})
   505  	require.NoError(t, err, "failed to download busybox")
   506  	defer reader.Close()
   507  
   508  	client, err := d.NewClient()
   509  	require.NoError(t, err, "failed to create client")
   510  	defer client.Close()
   511  
   512  	resp, err := client.ImageLoad(ctx, reader, true)
   513  	require.NoError(t, err, "failed to load busybox")
   514  	defer resp.Body.Close()
   515  }
   516  
   517  func (d *Daemon) queryRootDir() (string, error) {
   518  	// update daemon root by asking /info endpoint (to support user
   519  	// namespaced daemon with root remapped uid.gid directory)
   520  	clientConfig, err := d.getClientConfig()
   521  	if err != nil {
   522  		return "", err
   523  	}
   524  
   525  	client := &http.Client{
   526  		Transport: clientConfig.transport,
   527  	}
   528  
   529  	req, err := http.NewRequest("GET", "/info", nil)
   530  	if err != nil {
   531  		return "", err
   532  	}
   533  	req.Header.Set("Content-Type", "application/json")
   534  	req.URL.Host = clientConfig.addr
   535  	req.URL.Scheme = clientConfig.scheme
   536  
   537  	resp, err := client.Do(req)
   538  	if err != nil {
   539  		return "", err
   540  	}
   541  	body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
   542  		return resp.Body.Close()
   543  	})
   544  
   545  	type Info struct {
   546  		DockerRootDir string
   547  	}
   548  	var b []byte
   549  	var i Info
   550  	b, err = request.ReadBody(body)
   551  	if err == nil && resp.StatusCode == http.StatusOK {
   552  		// read the docker root dir
   553  		if err = json.Unmarshal(b, &i); err == nil {
   554  			return i.DockerRootDir, nil
   555  		}
   556  	}
   557  	return "", err
   558  }
   559  
   560  // Sock returns the socket path of the daemon
   561  func (d *Daemon) Sock() string {
   562  	return fmt.Sprintf("unix://" + d.sockPath())
   563  }
   564  
   565  func (d *Daemon) sockPath() string {
   566  	return filepath.Join(SockRoot, d.id+".sock")
   567  }
   568  
   569  // WaitRun waits for a container to be running for 10s
   570  func (d *Daemon) WaitRun(contID string) error {
   571  	args := []string{"--host", d.Sock()}
   572  	return WaitInspectWithArgs(d.dockerBinary, contID, "{{.State.Running}}", "true", 10*time.Second, args...)
   573  }
   574  
   575  // Info returns the info struct for this daemon
   576  func (d *Daemon) Info(t require.TestingT) types.Info {
   577  	apiclient, err := request.NewClientForHost(d.Sock())
   578  	require.NoError(t, err)
   579  	info, err := apiclient.Info(context.Background())
   580  	require.NoError(t, err)
   581  	return info
   582  }
   583  
   584  // Cmd executes a docker CLI command against this daemon.
   585  // Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version
   586  func (d *Daemon) Cmd(args ...string) (string, error) {
   587  	result := icmd.RunCmd(d.Command(args...))
   588  	return result.Combined(), result.Error
   589  }
   590  
   591  // Command creates a docker CLI command against this daemon, to be executed later.
   592  // Example: d.Command("version") creates a command to run "docker -H unix://path/to/unix.sock version"
   593  func (d *Daemon) Command(args ...string) icmd.Cmd {
   594  	return icmd.Command(d.dockerBinary, d.PrependHostArg(args)...)
   595  }
   596  
   597  // PrependHostArg prepend the specified arguments by the daemon host flags
   598  func (d *Daemon) PrependHostArg(args []string) []string {
   599  	for _, arg := range args {
   600  		if arg == "--host" || arg == "-H" {
   601  			return args
   602  		}
   603  	}
   604  	return append([]string{"--host", d.Sock()}, args...)
   605  }
   606  
   607  // SockRequest executes a socket request on a daemon and returns statuscode and output.
   608  func (d *Daemon) SockRequest(method, endpoint string, data interface{}) (int, []byte, error) {
   609  	jsonData := bytes.NewBuffer(nil)
   610  	if err := json.NewEncoder(jsonData).Encode(data); err != nil {
   611  		return -1, nil, err
   612  	}
   613  
   614  	res, body, err := d.SockRequestRaw(method, endpoint, jsonData, "application/json")
   615  	if err != nil {
   616  		return -1, nil, err
   617  	}
   618  	b, err := request.ReadBody(body)
   619  	return res.StatusCode, b, err
   620  }
   621  
   622  // SockRequestRaw executes a socket request on a daemon and returns an http
   623  // response and a reader for the output data.
   624  // Deprecated: use request package instead
   625  func (d *Daemon) SockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) {
   626  	return request.SockRequestRaw(method, endpoint, data, ct, d.Sock())
   627  }
   628  
   629  // LogFileName returns the path the daemon's log file
   630  func (d *Daemon) LogFileName() string {
   631  	return d.logFile.Name()
   632  }
   633  
   634  // GetIDByName returns the ID of an object (container, volume, …) given its name
   635  func (d *Daemon) GetIDByName(name string) (string, error) {
   636  	return d.inspectFieldWithError(name, "Id")
   637  }
   638  
   639  // ActiveContainers returns the list of ids of the currently running containers
   640  func (d *Daemon) ActiveContainers() (ids []string) {
   641  	// FIXME(vdemeester) shouldn't ignore the error
   642  	out, _ := d.Cmd("ps", "-q")
   643  	for _, id := range strings.Split(out, "\n") {
   644  		if id = strings.TrimSpace(id); id != "" {
   645  			ids = append(ids, id)
   646  		}
   647  	}
   648  	return
   649  }
   650  
   651  // ReadLogFile returns the content of the daemon log file
   652  func (d *Daemon) ReadLogFile() ([]byte, error) {
   653  	return ioutil.ReadFile(d.logFile.Name())
   654  }
   655  
   656  // InspectField returns the field filter by 'filter'
   657  func (d *Daemon) InspectField(name, filter string) (string, error) {
   658  	return d.inspectFilter(name, filter)
   659  }
   660  
   661  func (d *Daemon) inspectFilter(name, filter string) (string, error) {
   662  	format := fmt.Sprintf("{{%s}}", filter)
   663  	out, err := d.Cmd("inspect", "-f", format, name)
   664  	if err != nil {
   665  		return "", errors.Errorf("failed to inspect %s: %s", name, out)
   666  	}
   667  	return strings.TrimSpace(out), nil
   668  }
   669  
   670  func (d *Daemon) inspectFieldWithError(name, field string) (string, error) {
   671  	return d.inspectFilter(name, fmt.Sprintf(".%s", field))
   672  }
   673  
   674  // FindContainerIP returns the ip of the specified container
   675  func (d *Daemon) FindContainerIP(id string) (string, error) {
   676  	out, err := d.Cmd("inspect", "--format='{{ .NetworkSettings.Networks.bridge.IPAddress }}'", id)
   677  	if err != nil {
   678  		return "", err
   679  	}
   680  	return strings.Trim(out, " \r\n'"), nil
   681  }
   682  
   683  // BuildImageWithOut builds an image with the specified dockerfile and options and returns the output
   684  func (d *Daemon) BuildImageWithOut(name, dockerfile string, useCache bool, buildFlags ...string) (string, int, error) {
   685  	buildCmd := BuildImageCmdWithHost(d.dockerBinary, name, dockerfile, d.Sock(), useCache, buildFlags...)
   686  	result := icmd.RunCmd(icmd.Cmd{
   687  		Command: buildCmd.Args,
   688  		Env:     buildCmd.Env,
   689  		Dir:     buildCmd.Dir,
   690  		Stdin:   buildCmd.Stdin,
   691  		Stdout:  buildCmd.Stdout,
   692  	})
   693  	return result.Combined(), result.ExitCode, result.Error
   694  }
   695  
   696  // CheckActiveContainerCount returns the number of active containers
   697  // FIXME(vdemeester) should re-use ActivateContainers in some way
   698  func (d *Daemon) CheckActiveContainerCount(c *check.C) (interface{}, check.CommentInterface) {
   699  	out, err := d.Cmd("ps", "-q")
   700  	c.Assert(err, checker.IsNil)
   701  	if len(strings.TrimSpace(out)) == 0 {
   702  		return 0, nil
   703  	}
   704  	return len(strings.Split(strings.TrimSpace(out), "\n")), check.Commentf("output: %q", string(out))
   705  }
   706  
   707  // ReloadConfig asks the daemon to reload its configuration
   708  func (d *Daemon) ReloadConfig() error {
   709  	if d.cmd == nil || d.cmd.Process == nil {
   710  		return errors.New("daemon is not running")
   711  	}
   712  
   713  	errCh := make(chan error)
   714  	started := make(chan struct{})
   715  	go func() {
   716  		_, body, err := request.DoOnHost(d.Sock(), "/events", request.Method(http.MethodGet))
   717  		close(started)
   718  		if err != nil {
   719  			errCh <- err
   720  		}
   721  		defer body.Close()
   722  		dec := json.NewDecoder(body)
   723  		for {
   724  			var e events.Message
   725  			if err := dec.Decode(&e); err != nil {
   726  				errCh <- err
   727  				return
   728  			}
   729  			if e.Type != events.DaemonEventType {
   730  				continue
   731  			}
   732  			if e.Action != "reload" {
   733  				continue
   734  			}
   735  			close(errCh) // notify that we are done
   736  			return
   737  		}
   738  	}()
   739  
   740  	<-started
   741  	if err := signalDaemonReload(d.cmd.Process.Pid); err != nil {
   742  		return errors.Errorf("error signaling daemon reload: %v", err)
   743  	}
   744  	select {
   745  	case err := <-errCh:
   746  		if err != nil {
   747  			return errors.Errorf("error waiting for daemon reload event: %v", err)
   748  		}
   749  	case <-time.After(30 * time.Second):
   750  		return errors.New("timeout waiting for daemon reload event")
   751  	}
   752  	return nil
   753  }
   754  
   755  // NewClient creates new client based on daemon's socket path
   756  func (d *Daemon) NewClient() (*client.Client, error) {
   757  	httpClient, err := request.NewHTTPClient(d.Sock())
   758  	if err != nil {
   759  		return nil, err
   760  	}
   761  
   762  	return client.NewClient(d.Sock(), api.DefaultVersion, httpClient, nil)
   763  }
   764  
   765  // WaitInspectWithArgs waits for the specified expression to be equals to the specified expected string in the given time.
   766  // Deprecated: use cli.WaitCmd instead
   767  func WaitInspectWithArgs(dockerBinary, name, expr, expected string, timeout time.Duration, arg ...string) error {
   768  	after := time.After(timeout)
   769  
   770  	args := append(arg, "inspect", "-f", expr, name)
   771  	for {
   772  		result := icmd.RunCommand(dockerBinary, args...)
   773  		if result.Error != nil {
   774  			if !strings.Contains(strings.ToLower(result.Stderr()), "no such") {
   775  				return errors.Errorf("error executing docker inspect: %v\n%s",
   776  					result.Stderr(), result.Stdout())
   777  			}
   778  			select {
   779  			case <-after:
   780  				return result.Error
   781  			default:
   782  				time.Sleep(10 * time.Millisecond)
   783  				continue
   784  			}
   785  		}
   786  
   787  		out := strings.TrimSpace(result.Stdout())
   788  		if out == expected {
   789  			break
   790  		}
   791  
   792  		select {
   793  		case <-after:
   794  			return errors.Errorf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout)
   795  		default:
   796  		}
   797  
   798  		time.Sleep(100 * time.Millisecond)
   799  	}
   800  	return nil
   801  }
   802  
   803  // BuildImageCmdWithHost create a build command with the specified arguments.
   804  // Deprecated
   805  // FIXME(vdemeester) move this away
   806  func BuildImageCmdWithHost(dockerBinary, name, dockerfile, host string, useCache bool, buildFlags ...string) *exec.Cmd {
   807  	args := []string{}
   808  	if host != "" {
   809  		args = append(args, "--host", host)
   810  	}
   811  	args = append(args, "build", "-t", name)
   812  	if !useCache {
   813  		args = append(args, "--no-cache")
   814  	}
   815  	args = append(args, buildFlags...)
   816  	args = append(args, "-")
   817  	buildCmd := exec.Command(dockerBinary, args...)
   818  	buildCmd.Stdin = strings.NewReader(dockerfile)
   819  	return buildCmd
   820  }