github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/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  	return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
   352  }
   353  
   354  // Pid returns the pid of the daemon
   355  func (d *Daemon) Pid() int {
   356  	return d.cmd.Process.Pid
   357  }
   358  
   359  // Interrupt stops the daemon by sending it an Interrupt signal
   360  func (d *Daemon) Interrupt() error {
   361  	return d.Signal(os.Interrupt)
   362  }
   363  
   364  // Signal sends the specified signal to the daemon if running
   365  func (d *Daemon) Signal(signal os.Signal) error {
   366  	if d.cmd == nil || d.Wait == nil {
   367  		return errDaemonNotStarted
   368  	}
   369  	return d.cmd.Process.Signal(signal)
   370  }
   371  
   372  // DumpStackAndQuit sends SIGQUIT to the daemon, which triggers it to dump its
   373  // stack to its log file and exit
   374  // This is used primarily for gathering debug information on test timeout
   375  func (d *Daemon) DumpStackAndQuit() {
   376  	if d.cmd == nil || d.cmd.Process == nil {
   377  		return
   378  	}
   379  	SignalDaemonDump(d.cmd.Process.Pid)
   380  }
   381  
   382  // Stop will send a SIGINT every second and wait for the daemon to stop.
   383  // If it times out, a SIGKILL is sent.
   384  // Stop will not delete the daemon directory. If a purged daemon is needed,
   385  // instantiate a new one with NewDaemon.
   386  // If an error occurs while starting the daemon, the test will fail.
   387  func (d *Daemon) Stop(t testingT) {
   388  	err := d.StopWithError()
   389  	if err != nil {
   390  		if err != errDaemonNotStarted {
   391  			t.Fatalf("Error while stopping the daemon %s : %v", d.id, err)
   392  		} else {
   393  			t.Logf("Daemon %s is not started", d.id)
   394  		}
   395  	}
   396  }
   397  
   398  // StopWithError will send a SIGINT every second and wait for the daemon to stop.
   399  // If it timeouts, a SIGKILL is sent.
   400  // Stop will not delete the daemon directory. If a purged daemon is needed,
   401  // instantiate a new one with NewDaemon.
   402  func (d *Daemon) StopWithError() error {
   403  	if d.cmd == nil || d.Wait == nil {
   404  		return errDaemonNotStarted
   405  	}
   406  
   407  	defer func() {
   408  		d.logFile.Close()
   409  		d.cmd = nil
   410  	}()
   411  
   412  	i := 1
   413  	tick := time.Tick(time.Second)
   414  
   415  	if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
   416  		if strings.Contains(err.Error(), "os: process already finished") {
   417  			return errDaemonNotStarted
   418  		}
   419  		return errors.Errorf("could not send signal: %v", err)
   420  	}
   421  out1:
   422  	for {
   423  		select {
   424  		case err := <-d.Wait:
   425  			return err
   426  		case <-time.After(20 * time.Second):
   427  			// time for stopping jobs and run onShutdown hooks
   428  			d.log.Logf("[%s] daemon started", d.id)
   429  			break out1
   430  		}
   431  	}
   432  
   433  out2:
   434  	for {
   435  		select {
   436  		case err := <-d.Wait:
   437  			return err
   438  		case <-tick:
   439  			i++
   440  			if i > 5 {
   441  				d.log.Logf("tried to interrupt daemon for %d times, now try to kill it", i)
   442  				break out2
   443  			}
   444  			d.log.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid)
   445  			if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
   446  				return errors.Errorf("could not send signal: %v", err)
   447  			}
   448  		}
   449  	}
   450  
   451  	if err := d.cmd.Process.Kill(); err != nil {
   452  		d.log.Logf("Could not kill daemon: %v", err)
   453  		return err
   454  	}
   455  
   456  	d.cmd.Wait()
   457  
   458  	return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
   459  }
   460  
   461  // Restart will restart the daemon by first stopping it and the starting it.
   462  // If an error occurs while starting the daemon, the test will fail.
   463  func (d *Daemon) Restart(t testingT, args ...string) {
   464  	d.Stop(t)
   465  	d.handleUserns()
   466  	d.Start(t, args...)
   467  }
   468  
   469  // RestartWithError will restart the daemon by first stopping it and then starting it.
   470  func (d *Daemon) RestartWithError(arg ...string) error {
   471  	if err := d.StopWithError(); err != nil {
   472  		return err
   473  	}
   474  	d.handleUserns()
   475  	return d.StartWithError(arg...)
   476  }
   477  
   478  func (d *Daemon) handleUserns() {
   479  	// in the case of tests running a user namespace-enabled daemon, we have resolved
   480  	// d.Root to be the actual final path of the graph dir after the "uid.gid" of
   481  	// remapped root is added--we need to subtract it from the path before calling
   482  	// start or else we will continue making subdirectories rather than truly restarting
   483  	// with the same location/root:
   484  	if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
   485  		d.Root = filepath.Dir(d.Root)
   486  	}
   487  }
   488  
   489  // LoadBusybox image into the daemon
   490  func (d *Daemon) LoadBusybox(t testingT) {
   491  	clientHost, err := client.NewEnvClient()
   492  	require.NoError(t, err, "failed to create client")
   493  	defer clientHost.Close()
   494  
   495  	ctx := context.Background()
   496  	reader, err := clientHost.ImageSave(ctx, []string{"busybox:latest"})
   497  	require.NoError(t, err, "failed to download busybox")
   498  	defer reader.Close()
   499  
   500  	client, err := d.NewClient()
   501  	require.NoError(t, err, "failed to create client")
   502  	defer client.Close()
   503  
   504  	resp, err := client.ImageLoad(ctx, reader, true)
   505  	require.NoError(t, err, "failed to load busybox")
   506  	defer resp.Body.Close()
   507  }
   508  
   509  func (d *Daemon) queryRootDir() (string, error) {
   510  	// update daemon root by asking /info endpoint (to support user
   511  	// namespaced daemon with root remapped uid.gid directory)
   512  	clientConfig, err := d.getClientConfig()
   513  	if err != nil {
   514  		return "", err
   515  	}
   516  
   517  	client := &http.Client{
   518  		Transport: clientConfig.transport,
   519  	}
   520  
   521  	req, err := http.NewRequest("GET", "/info", nil)
   522  	if err != nil {
   523  		return "", err
   524  	}
   525  	req.Header.Set("Content-Type", "application/json")
   526  	req.URL.Host = clientConfig.addr
   527  	req.URL.Scheme = clientConfig.scheme
   528  
   529  	resp, err := client.Do(req)
   530  	if err != nil {
   531  		return "", err
   532  	}
   533  	body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
   534  		return resp.Body.Close()
   535  	})
   536  
   537  	type Info struct {
   538  		DockerRootDir string
   539  	}
   540  	var b []byte
   541  	var i Info
   542  	b, err = request.ReadBody(body)
   543  	if err == nil && resp.StatusCode == http.StatusOK {
   544  		// read the docker root dir
   545  		if err = json.Unmarshal(b, &i); err == nil {
   546  			return i.DockerRootDir, nil
   547  		}
   548  	}
   549  	return "", err
   550  }
   551  
   552  // Sock returns the socket path of the daemon
   553  func (d *Daemon) Sock() string {
   554  	return fmt.Sprintf("unix://" + d.sockPath())
   555  }
   556  
   557  func (d *Daemon) sockPath() string {
   558  	return filepath.Join(SockRoot, d.id+".sock")
   559  }
   560  
   561  // WaitRun waits for a container to be running for 10s
   562  func (d *Daemon) WaitRun(contID string) error {
   563  	args := []string{"--host", d.Sock()}
   564  	return WaitInspectWithArgs(d.dockerBinary, contID, "{{.State.Running}}", "true", 10*time.Second, args...)
   565  }
   566  
   567  // Info returns the info struct for this daemon
   568  func (d *Daemon) Info(t require.TestingT) types.Info {
   569  	apiclient, err := request.NewClientForHost(d.Sock())
   570  	require.NoError(t, err)
   571  	info, err := apiclient.Info(context.Background())
   572  	require.NoError(t, err)
   573  	return info
   574  }
   575  
   576  // Cmd executes a docker CLI command against this daemon.
   577  // Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version
   578  func (d *Daemon) Cmd(args ...string) (string, error) {
   579  	result := icmd.RunCmd(d.Command(args...))
   580  	return result.Combined(), result.Error
   581  }
   582  
   583  // Command creates a docker CLI command against this daemon, to be executed later.
   584  // Example: d.Command("version") creates a command to run "docker -H unix://path/to/unix.sock version"
   585  func (d *Daemon) Command(args ...string) icmd.Cmd {
   586  	return icmd.Command(d.dockerBinary, d.PrependHostArg(args)...)
   587  }
   588  
   589  // PrependHostArg prepend the specified arguments by the daemon host flags
   590  func (d *Daemon) PrependHostArg(args []string) []string {
   591  	for _, arg := range args {
   592  		if arg == "--host" || arg == "-H" {
   593  			return args
   594  		}
   595  	}
   596  	return append([]string{"--host", d.Sock()}, args...)
   597  }
   598  
   599  // SockRequest executes a socket request on a daemon and returns statuscode and output.
   600  func (d *Daemon) SockRequest(method, endpoint string, data interface{}) (int, []byte, error) {
   601  	jsonData := bytes.NewBuffer(nil)
   602  	if err := json.NewEncoder(jsonData).Encode(data); err != nil {
   603  		return -1, nil, err
   604  	}
   605  
   606  	res, body, err := d.SockRequestRaw(method, endpoint, jsonData, "application/json")
   607  	if err != nil {
   608  		return -1, nil, err
   609  	}
   610  	b, err := request.ReadBody(body)
   611  	return res.StatusCode, b, err
   612  }
   613  
   614  // SockRequestRaw executes a socket request on a daemon and returns an http
   615  // response and a reader for the output data.
   616  // Deprecated: use request package instead
   617  func (d *Daemon) SockRequestRaw(method, endpoint string, data io.Reader, ct string) (*http.Response, io.ReadCloser, error) {
   618  	return request.SockRequestRaw(method, endpoint, data, ct, d.Sock())
   619  }
   620  
   621  // LogFileName returns the path the daemon's log file
   622  func (d *Daemon) LogFileName() string {
   623  	return d.logFile.Name()
   624  }
   625  
   626  // GetIDByName returns the ID of an object (container, volume, …) given its name
   627  func (d *Daemon) GetIDByName(name string) (string, error) {
   628  	return d.inspectFieldWithError(name, "Id")
   629  }
   630  
   631  // ActiveContainers returns the list of ids of the currently running containers
   632  func (d *Daemon) ActiveContainers() (ids []string) {
   633  	// FIXME(vdemeester) shouldn't ignore the error
   634  	out, _ := d.Cmd("ps", "-q")
   635  	for _, id := range strings.Split(out, "\n") {
   636  		if id = strings.TrimSpace(id); id != "" {
   637  			ids = append(ids, id)
   638  		}
   639  	}
   640  	return
   641  }
   642  
   643  // ReadLogFile returns the content of the daemon log file
   644  func (d *Daemon) ReadLogFile() ([]byte, error) {
   645  	return ioutil.ReadFile(d.logFile.Name())
   646  }
   647  
   648  // InspectField returns the field filter by 'filter'
   649  func (d *Daemon) InspectField(name, filter string) (string, error) {
   650  	return d.inspectFilter(name, filter)
   651  }
   652  
   653  func (d *Daemon) inspectFilter(name, filter string) (string, error) {
   654  	format := fmt.Sprintf("{{%s}}", filter)
   655  	out, err := d.Cmd("inspect", "-f", format, name)
   656  	if err != nil {
   657  		return "", errors.Errorf("failed to inspect %s: %s", name, out)
   658  	}
   659  	return strings.TrimSpace(out), nil
   660  }
   661  
   662  func (d *Daemon) inspectFieldWithError(name, field string) (string, error) {
   663  	return d.inspectFilter(name, fmt.Sprintf(".%s", field))
   664  }
   665  
   666  // FindContainerIP returns the ip of the specified container
   667  func (d *Daemon) FindContainerIP(id string) (string, error) {
   668  	out, err := d.Cmd("inspect", "--format='{{ .NetworkSettings.Networks.bridge.IPAddress }}'", id)
   669  	if err != nil {
   670  		return "", err
   671  	}
   672  	return strings.Trim(out, " \r\n'"), nil
   673  }
   674  
   675  // BuildImageWithOut builds an image with the specified dockerfile and options and returns the output
   676  func (d *Daemon) BuildImageWithOut(name, dockerfile string, useCache bool, buildFlags ...string) (string, int, error) {
   677  	buildCmd := BuildImageCmdWithHost(d.dockerBinary, name, dockerfile, d.Sock(), useCache, buildFlags...)
   678  	result := icmd.RunCmd(icmd.Cmd{
   679  		Command: buildCmd.Args,
   680  		Env:     buildCmd.Env,
   681  		Dir:     buildCmd.Dir,
   682  		Stdin:   buildCmd.Stdin,
   683  		Stdout:  buildCmd.Stdout,
   684  	})
   685  	return result.Combined(), result.ExitCode, result.Error
   686  }
   687  
   688  // CheckActiveContainerCount returns the number of active containers
   689  // FIXME(vdemeester) should re-use ActivateContainers in some way
   690  func (d *Daemon) CheckActiveContainerCount(c *check.C) (interface{}, check.CommentInterface) {
   691  	out, err := d.Cmd("ps", "-q")
   692  	c.Assert(err, checker.IsNil)
   693  	if len(strings.TrimSpace(out)) == 0 {
   694  		return 0, nil
   695  	}
   696  	return len(strings.Split(strings.TrimSpace(out), "\n")), check.Commentf("output: %q", string(out))
   697  }
   698  
   699  // ReloadConfig asks the daemon to reload its configuration
   700  func (d *Daemon) ReloadConfig() error {
   701  	if d.cmd == nil || d.cmd.Process == nil {
   702  		return errors.New("daemon is not running")
   703  	}
   704  
   705  	errCh := make(chan error)
   706  	started := make(chan struct{})
   707  	go func() {
   708  		_, body, err := request.DoOnHost(d.Sock(), "/events", request.Method(http.MethodGet))
   709  		close(started)
   710  		if err != nil {
   711  			errCh <- err
   712  		}
   713  		defer body.Close()
   714  		dec := json.NewDecoder(body)
   715  		for {
   716  			var e events.Message
   717  			if err := dec.Decode(&e); err != nil {
   718  				errCh <- err
   719  				return
   720  			}
   721  			if e.Type != events.DaemonEventType {
   722  				continue
   723  			}
   724  			if e.Action != "reload" {
   725  				continue
   726  			}
   727  			close(errCh) // notify that we are done
   728  			return
   729  		}
   730  	}()
   731  
   732  	<-started
   733  	if err := signalDaemonReload(d.cmd.Process.Pid); err != nil {
   734  		return errors.Errorf("error signaling daemon reload: %v", err)
   735  	}
   736  	select {
   737  	case err := <-errCh:
   738  		if err != nil {
   739  			return errors.Errorf("error waiting for daemon reload event: %v", err)
   740  		}
   741  	case <-time.After(30 * time.Second):
   742  		return errors.New("timeout waiting for daemon reload event")
   743  	}
   744  	return nil
   745  }
   746  
   747  // NewClient creates new client based on daemon's socket path
   748  func (d *Daemon) NewClient() (*client.Client, error) {
   749  	httpClient, err := request.NewHTTPClient(d.Sock())
   750  	if err != nil {
   751  		return nil, err
   752  	}
   753  
   754  	return client.NewClient(d.Sock(), api.DefaultVersion, httpClient, nil)
   755  }
   756  
   757  // WaitInspectWithArgs waits for the specified expression to be equals to the specified expected string in the given time.
   758  // Deprecated: use cli.WaitCmd instead
   759  func WaitInspectWithArgs(dockerBinary, name, expr, expected string, timeout time.Duration, arg ...string) error {
   760  	after := time.After(timeout)
   761  
   762  	args := append(arg, "inspect", "-f", expr, name)
   763  	for {
   764  		result := icmd.RunCommand(dockerBinary, args...)
   765  		if result.Error != nil {
   766  			if !strings.Contains(strings.ToLower(result.Stderr()), "no such") {
   767  				return errors.Errorf("error executing docker inspect: %v\n%s",
   768  					result.Stderr(), result.Stdout())
   769  			}
   770  			select {
   771  			case <-after:
   772  				return result.Error
   773  			default:
   774  				time.Sleep(10 * time.Millisecond)
   775  				continue
   776  			}
   777  		}
   778  
   779  		out := strings.TrimSpace(result.Stdout())
   780  		if out == expected {
   781  			break
   782  		}
   783  
   784  		select {
   785  		case <-after:
   786  			return errors.Errorf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout)
   787  		default:
   788  		}
   789  
   790  		time.Sleep(100 * time.Millisecond)
   791  	}
   792  	return nil
   793  }
   794  
   795  // BuildImageCmdWithHost create a build command with the specified arguments.
   796  // Deprecated
   797  // FIXME(vdemeester) move this away
   798  func BuildImageCmdWithHost(dockerBinary, name, dockerfile, host string, useCache bool, buildFlags ...string) *exec.Cmd {
   799  	args := []string{}
   800  	if host != "" {
   801  		args = append(args, "--host", host)
   802  	}
   803  	args = append(args, "build", "-t", name)
   804  	if !useCache {
   805  		args = append(args, "--no-cache")
   806  	}
   807  	args = append(args, buildFlags...)
   808  	args = append(args, "-")
   809  	buildCmd := exec.Command(dockerBinary, args...)
   810  	buildCmd.Stdin = strings.NewReader(dockerfile)
   811  	return buildCmd
   812  }