github.com/cookieai-jar/moby@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 }