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 }