github.com/mizzy/docker@v1.5.0/api/client/commands.go (about) 1 package client 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/base64" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "net/http" 13 "net/url" 14 "os" 15 "os/exec" 16 "path" 17 "path/filepath" 18 "runtime" 19 "sort" 20 "strconv" 21 "strings" 22 "sync" 23 "text/tabwriter" 24 "text/template" 25 "time" 26 27 log "github.com/Sirupsen/logrus" 28 "github.com/docker/docker/api" 29 "github.com/docker/docker/api/stats" 30 "github.com/docker/docker/dockerversion" 31 "github.com/docker/docker/engine" 32 "github.com/docker/docker/graph" 33 "github.com/docker/docker/nat" 34 "github.com/docker/docker/opts" 35 "github.com/docker/docker/pkg/archive" 36 "github.com/docker/docker/pkg/fileutils" 37 flag "github.com/docker/docker/pkg/mflag" 38 "github.com/docker/docker/pkg/parsers" 39 "github.com/docker/docker/pkg/parsers/filters" 40 "github.com/docker/docker/pkg/promise" 41 "github.com/docker/docker/pkg/signal" 42 "github.com/docker/docker/pkg/symlink" 43 "github.com/docker/docker/pkg/term" 44 "github.com/docker/docker/pkg/timeutils" 45 "github.com/docker/docker/pkg/units" 46 "github.com/docker/docker/pkg/urlutil" 47 "github.com/docker/docker/registry" 48 "github.com/docker/docker/runconfig" 49 "github.com/docker/docker/utils" 50 ) 51 52 const ( 53 tarHeaderSize = 512 54 ) 55 56 func (cli *DockerCli) CmdHelp(args ...string) error { 57 if len(args) > 1 { 58 method, exists := cli.getMethod(args[:2]...) 59 if exists { 60 method("--help") 61 return nil 62 } 63 } 64 if len(args) > 0 { 65 method, exists := cli.getMethod(args[0]) 66 if !exists { 67 fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0]) 68 os.Exit(1) 69 } else { 70 method("--help") 71 return nil 72 } 73 } 74 75 flag.Usage() 76 77 return nil 78 } 79 80 func (cli *DockerCli) CmdBuild(args ...string) error { 81 cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH", true) 82 tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success") 83 suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers") 84 noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image") 85 rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build") 86 forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds") 87 pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image") 88 dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile(Default is 'Dockerfile' at context root)") 89 90 cmd.Require(flag.Exact, 1) 91 92 utils.ParseFlags(cmd, args, true) 93 94 var ( 95 context archive.Archive 96 isRemote bool 97 err error 98 ) 99 100 _, err = exec.LookPath("git") 101 hasGit := err == nil 102 if cmd.Arg(0) == "-" { 103 // As a special case, 'docker build -' will build from either an empty context with the 104 // contents of stdin as a Dockerfile, or a tar-ed context from stdin. 105 buf := bufio.NewReader(cli.in) 106 magic, err := buf.Peek(tarHeaderSize) 107 if err != nil && err != io.EOF { 108 return fmt.Errorf("failed to peek context header from STDIN: %v", err) 109 } 110 if !archive.IsArchive(magic) { 111 dockerfile, err := ioutil.ReadAll(buf) 112 if err != nil { 113 return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err) 114 } 115 if *dockerfileName == "" { 116 *dockerfileName = api.DefaultDockerfileName 117 } 118 context, err = archive.Generate(*dockerfileName, string(dockerfile)) 119 } else { 120 context = ioutil.NopCloser(buf) 121 } 122 } else if urlutil.IsURL(cmd.Arg(0)) && (!urlutil.IsGitURL(cmd.Arg(0)) || !hasGit) { 123 isRemote = true 124 } else { 125 root := cmd.Arg(0) 126 if urlutil.IsGitURL(root) { 127 remoteURL := cmd.Arg(0) 128 if !urlutil.IsGitTransport(remoteURL) { 129 remoteURL = "https://" + remoteURL 130 } 131 132 root, err = ioutil.TempDir("", "docker-build-git") 133 if err != nil { 134 return err 135 } 136 defer os.RemoveAll(root) 137 138 if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil { 139 return fmt.Errorf("Error trying to use git: %s (%s)", err, output) 140 } 141 } 142 if _, err := os.Stat(root); err != nil { 143 return err 144 } 145 146 absRoot, err := filepath.Abs(root) 147 if err != nil { 148 return err 149 } 150 151 filename := *dockerfileName // path to Dockerfile 152 153 if *dockerfileName == "" { 154 // No -f/--file was specified so use the default 155 *dockerfileName = api.DefaultDockerfileName 156 filename = path.Join(absRoot, *dockerfileName) 157 } 158 159 origDockerfile := *dockerfileName // used for error msg 160 161 if filename, err = filepath.Abs(filename); err != nil { 162 return err 163 } 164 165 // Verify that 'filename' is within the build context 166 filename, err = symlink.FollowSymlinkInScope(filename, absRoot) 167 if err != nil { 168 return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root) 169 } 170 171 // Now reset the dockerfileName to be relative to the build context 172 *dockerfileName, err = filepath.Rel(absRoot, filename) 173 if err != nil { 174 return err 175 } 176 177 if _, err = os.Lstat(filename); os.IsNotExist(err) { 178 return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile) 179 } 180 var includes = []string{"."} 181 182 excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore")) 183 if err != nil { 184 return err 185 } 186 187 // If .dockerignore mentions .dockerignore or the Dockerfile 188 // then make sure we send both files over to the daemon 189 // because Dockerfile is, obviously, needed no matter what, and 190 // .dockerignore is needed to know if either one needs to be 191 // removed. The deamon will remove them for us, if needed, after it 192 // parses the Dockerfile. 193 keepThem1, _ := fileutils.Matches(".dockerignore", excludes) 194 keepThem2, _ := fileutils.Matches(*dockerfileName, excludes) 195 if keepThem1 || keepThem2 { 196 includes = append(includes, ".dockerignore", *dockerfileName) 197 } 198 199 if err = utils.ValidateContextDirectory(root, excludes); err != nil { 200 return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err) 201 } 202 options := &archive.TarOptions{ 203 Compression: archive.Uncompressed, 204 ExcludePatterns: excludes, 205 IncludeFiles: includes, 206 } 207 context, err = archive.TarWithOptions(root, options) 208 if err != nil { 209 return err 210 } 211 } 212 var body io.Reader 213 // Setup an upload progress bar 214 // FIXME: ProgressReader shouldn't be this annoying to use 215 if context != nil { 216 sf := utils.NewStreamFormatter(false) 217 body = utils.ProgressReader(context, 0, cli.out, sf, true, "", "Sending build context to Docker daemon") 218 } 219 // Send the build context 220 v := &url.Values{} 221 222 //Check if the given image name can be resolved 223 if *tag != "" { 224 repository, tag := parsers.ParseRepositoryTag(*tag) 225 if err := registry.ValidateRepositoryName(repository); err != nil { 226 return err 227 } 228 if len(tag) > 0 { 229 if err := graph.ValidateTagName(tag); err != nil { 230 return err 231 } 232 } 233 } 234 235 v.Set("t", *tag) 236 237 if *suppressOutput { 238 v.Set("q", "1") 239 } 240 if isRemote { 241 v.Set("remote", cmd.Arg(0)) 242 } 243 if *noCache { 244 v.Set("nocache", "1") 245 } 246 if *rm { 247 v.Set("rm", "1") 248 } else { 249 v.Set("rm", "0") 250 } 251 252 if *forceRm { 253 v.Set("forcerm", "1") 254 } 255 256 if *pull { 257 v.Set("pull", "1") 258 } 259 260 v.Set("dockerfile", *dockerfileName) 261 262 cli.LoadConfigFile() 263 264 headers := http.Header(make(map[string][]string)) 265 buf, err := json.Marshal(cli.configFile) 266 if err != nil { 267 return err 268 } 269 headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) 270 271 if context != nil { 272 headers.Set("Content-Type", "application/tar") 273 } 274 err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers) 275 if jerr, ok := err.(*utils.JSONError); ok { 276 // If no error code is set, default to 1 277 if jerr.Code == 0 { 278 jerr.Code = 1 279 } 280 return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code} 281 } 282 return err 283 } 284 285 // 'docker login': login / register a user to registry service. 286 func (cli *DockerCli) CmdLogin(args ...string) error { 287 cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true) 288 cmd.Require(flag.Max, 1) 289 290 var username, password, email string 291 292 cmd.StringVar(&username, []string{"u", "-username"}, "", "Username") 293 cmd.StringVar(&password, []string{"p", "-password"}, "", "Password") 294 cmd.StringVar(&email, []string{"e", "-email"}, "", "Email") 295 296 utils.ParseFlags(cmd, args, true) 297 298 serverAddress := registry.IndexServerAddress() 299 if len(cmd.Args()) > 0 { 300 serverAddress = cmd.Arg(0) 301 } 302 303 promptDefault := func(prompt string, configDefault string) { 304 if configDefault == "" { 305 fmt.Fprintf(cli.out, "%s: ", prompt) 306 } else { 307 fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) 308 } 309 } 310 311 readInput := func(in io.Reader, out io.Writer) string { 312 reader := bufio.NewReader(in) 313 line, _, err := reader.ReadLine() 314 if err != nil { 315 fmt.Fprintln(out, err.Error()) 316 os.Exit(1) 317 } 318 return string(line) 319 } 320 321 cli.LoadConfigFile() 322 authconfig, ok := cli.configFile.Configs[serverAddress] 323 if !ok { 324 authconfig = registry.AuthConfig{} 325 } 326 327 if username == "" { 328 promptDefault("Username", authconfig.Username) 329 username = readInput(cli.in, cli.out) 330 if username == "" { 331 username = authconfig.Username 332 } 333 } 334 // Assume that a different username means they may not want to use 335 // the password or email from the config file, so prompt them 336 if username != authconfig.Username { 337 if password == "" { 338 oldState, err := term.SaveState(cli.inFd) 339 if err != nil { 340 return err 341 } 342 fmt.Fprintf(cli.out, "Password: ") 343 term.DisableEcho(cli.inFd, oldState) 344 345 password = readInput(cli.in, cli.out) 346 fmt.Fprint(cli.out, "\n") 347 348 term.RestoreTerminal(cli.inFd, oldState) 349 if password == "" { 350 return fmt.Errorf("Error : Password Required") 351 } 352 } 353 354 if email == "" { 355 promptDefault("Email", authconfig.Email) 356 email = readInput(cli.in, cli.out) 357 if email == "" { 358 email = authconfig.Email 359 } 360 } 361 } else { 362 // However, if they don't override the username use the 363 // password or email from the cmd line if specified. IOW, allow 364 // then to change/overide them. And if not specified, just 365 // use what's in the config file 366 if password == "" { 367 password = authconfig.Password 368 } 369 if email == "" { 370 email = authconfig.Email 371 } 372 } 373 authconfig.Username = username 374 authconfig.Password = password 375 authconfig.Email = email 376 authconfig.ServerAddress = serverAddress 377 cli.configFile.Configs[serverAddress] = authconfig 378 379 stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false) 380 if statusCode == 401 { 381 delete(cli.configFile.Configs, serverAddress) 382 registry.SaveConfig(cli.configFile) 383 return err 384 } 385 if err != nil { 386 return err 387 } 388 var out2 engine.Env 389 err = out2.Decode(stream) 390 if err != nil { 391 cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME")) 392 return err 393 } 394 registry.SaveConfig(cli.configFile) 395 if out2.Get("Status") != "" { 396 fmt.Fprintf(cli.out, "%s\n", out2.Get("Status")) 397 } 398 return nil 399 } 400 401 // log out from a Docker registry 402 func (cli *DockerCli) CmdLogout(args ...string) error { 403 cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true) 404 cmd.Require(flag.Max, 1) 405 406 utils.ParseFlags(cmd, args, false) 407 serverAddress := registry.IndexServerAddress() 408 if len(cmd.Args()) > 0 { 409 serverAddress = cmd.Arg(0) 410 } 411 412 cli.LoadConfigFile() 413 if _, ok := cli.configFile.Configs[serverAddress]; !ok { 414 fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress) 415 } else { 416 fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress) 417 delete(cli.configFile.Configs, serverAddress) 418 419 if err := registry.SaveConfig(cli.configFile); err != nil { 420 return fmt.Errorf("Failed to save docker config: %v", err) 421 } 422 } 423 return nil 424 } 425 426 // 'docker wait': block until a container stops 427 func (cli *DockerCli) CmdWait(args ...string) error { 428 cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.", true) 429 cmd.Require(flag.Min, 1) 430 431 utils.ParseFlags(cmd, args, true) 432 433 var encounteredError error 434 for _, name := range cmd.Args() { 435 status, err := waitForExit(cli, name) 436 if err != nil { 437 fmt.Fprintf(cli.err, "%s\n", err) 438 encounteredError = fmt.Errorf("Error: failed to wait one or more containers") 439 } else { 440 fmt.Fprintf(cli.out, "%d\n", status) 441 } 442 } 443 return encounteredError 444 } 445 446 // 'docker version': show version information 447 func (cli *DockerCli) CmdVersion(args ...string) error { 448 cmd := cli.Subcmd("version", "", "Show the Docker version information.", true) 449 cmd.Require(flag.Exact, 0) 450 451 utils.ParseFlags(cmd, args, false) 452 453 if dockerversion.VERSION != "" { 454 fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION) 455 } 456 fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION) 457 fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version()) 458 if dockerversion.GITCOMMIT != "" { 459 fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT) 460 } 461 fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH) 462 463 body, _, err := readBody(cli.call("GET", "/version", nil, false)) 464 if err != nil { 465 return err 466 } 467 468 out := engine.NewOutput() 469 remoteVersion, err := out.AddEnv() 470 if err != nil { 471 log.Errorf("Error reading remote version: %s", err) 472 return err 473 } 474 if _, err := out.Write(body); err != nil { 475 log.Errorf("Error reading remote version: %s", err) 476 return err 477 } 478 out.Close() 479 fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version")) 480 if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" { 481 fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion) 482 } 483 fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion")) 484 fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit")) 485 return nil 486 } 487 488 // 'docker info': display system-wide information. 489 func (cli *DockerCli) CmdInfo(args ...string) error { 490 cmd := cli.Subcmd("info", "", "Display system-wide information", true) 491 cmd.Require(flag.Exact, 0) 492 utils.ParseFlags(cmd, args, false) 493 494 body, _, err := readBody(cli.call("GET", "/info", nil, false)) 495 if err != nil { 496 return err 497 } 498 499 out := engine.NewOutput() 500 remoteInfo, err := out.AddEnv() 501 if err != nil { 502 return err 503 } 504 505 if _, err := out.Write(body); err != nil { 506 log.Errorf("Error reading remote info: %s", err) 507 return err 508 } 509 out.Close() 510 511 if remoteInfo.Exists("Containers") { 512 fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers")) 513 } 514 if remoteInfo.Exists("Images") { 515 fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images")) 516 } 517 if remoteInfo.Exists("Driver") { 518 fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver")) 519 } 520 if remoteInfo.Exists("DriverStatus") { 521 var driverStatus [][2]string 522 if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil { 523 return err 524 } 525 for _, pair := range driverStatus { 526 fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1]) 527 } 528 } 529 if remoteInfo.Exists("ExecutionDriver") { 530 fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver")) 531 } 532 if remoteInfo.Exists("KernelVersion") { 533 fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion")) 534 } 535 if remoteInfo.Exists("OperatingSystem") { 536 fmt.Fprintf(cli.out, "Operating System: %s\n", remoteInfo.Get("OperatingSystem")) 537 } 538 if remoteInfo.Exists("NCPU") { 539 fmt.Fprintf(cli.out, "CPUs: %d\n", remoteInfo.GetInt("NCPU")) 540 } 541 if remoteInfo.Exists("MemTotal") { 542 fmt.Fprintf(cli.out, "Total Memory: %s\n", units.BytesSize(float64(remoteInfo.GetInt64("MemTotal")))) 543 } 544 if remoteInfo.Exists("Name") { 545 fmt.Fprintf(cli.out, "Name: %s\n", remoteInfo.Get("Name")) 546 } 547 if remoteInfo.Exists("ID") { 548 fmt.Fprintf(cli.out, "ID: %s\n", remoteInfo.Get("ID")) 549 } 550 551 if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" { 552 if remoteInfo.Exists("Debug") { 553 fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug")) 554 } 555 fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "") 556 if remoteInfo.Exists("NFd") { 557 fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd")) 558 } 559 if remoteInfo.Exists("NGoroutines") { 560 fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines")) 561 } 562 if remoteInfo.Exists("NEventsListener") { 563 fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener")) 564 } 565 if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" { 566 fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1) 567 } 568 if initPath := remoteInfo.Get("InitPath"); initPath != "" { 569 fmt.Fprintf(cli.out, "Init Path: %s\n", initPath) 570 } 571 if root := remoteInfo.Get("DockerRootDir"); root != "" { 572 fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", root) 573 } 574 } 575 576 if len(remoteInfo.GetList("IndexServerAddress")) != 0 { 577 cli.LoadConfigFile() 578 u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username 579 if len(u) > 0 { 580 fmt.Fprintf(cli.out, "Username: %v\n", u) 581 fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress")) 582 } 583 } 584 if remoteInfo.Exists("MemoryLimit") && !remoteInfo.GetBool("MemoryLimit") { 585 fmt.Fprintf(cli.err, "WARNING: No memory limit support\n") 586 } 587 if remoteInfo.Exists("SwapLimit") && !remoteInfo.GetBool("SwapLimit") { 588 fmt.Fprintf(cli.err, "WARNING: No swap limit support\n") 589 } 590 if remoteInfo.Exists("IPv4Forwarding") && !remoteInfo.GetBool("IPv4Forwarding") { 591 fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n") 592 } 593 if remoteInfo.Exists("Labels") { 594 fmt.Fprintln(cli.out, "Labels:") 595 for _, attribute := range remoteInfo.GetList("Labels") { 596 fmt.Fprintf(cli.out, " %s\n", attribute) 597 } 598 } 599 600 return nil 601 } 602 603 func (cli *DockerCli) CmdStop(args ...string) error { 604 cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period", true) 605 nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.") 606 cmd.Require(flag.Min, 1) 607 608 utils.ParseFlags(cmd, args, true) 609 610 v := url.Values{} 611 v.Set("t", strconv.Itoa(*nSeconds)) 612 613 var encounteredError error 614 for _, name := range cmd.Args() { 615 _, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false)) 616 if err != nil { 617 fmt.Fprintf(cli.err, "%s\n", err) 618 encounteredError = fmt.Errorf("Error: failed to stop one or more containers") 619 } else { 620 fmt.Fprintf(cli.out, "%s\n", name) 621 } 622 } 623 return encounteredError 624 } 625 626 func (cli *DockerCli) CmdRestart(args ...string) error { 627 cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true) 628 nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default is 10 seconds.") 629 cmd.Require(flag.Min, 1) 630 631 utils.ParseFlags(cmd, args, true) 632 633 v := url.Values{} 634 v.Set("t", strconv.Itoa(*nSeconds)) 635 636 var encounteredError error 637 for _, name := range cmd.Args() { 638 _, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false)) 639 if err != nil { 640 fmt.Fprintf(cli.err, "%s\n", err) 641 encounteredError = fmt.Errorf("Error: failed to restart one or more containers") 642 } else { 643 fmt.Fprintf(cli.out, "%s\n", name) 644 } 645 } 646 return encounteredError 647 } 648 649 func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal { 650 sigc := make(chan os.Signal, 128) 651 signal.CatchAll(sigc) 652 go func() { 653 for s := range sigc { 654 if s == signal.SIGCHLD { 655 continue 656 } 657 var sig string 658 for sigStr, sigN := range signal.SignalMap { 659 if sigN == s { 660 sig = sigStr 661 break 662 } 663 } 664 if sig == "" { 665 log.Errorf("Unsupported signal: %v. Discarding.", s) 666 } 667 if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil { 668 log.Debugf("Error sending signal: %s", err) 669 } 670 } 671 }() 672 return sigc 673 } 674 675 func (cli *DockerCli) CmdStart(args ...string) error { 676 var ( 677 cErr chan error 678 tty bool 679 680 cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container", true) 681 attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's STDOUT and STDERR and forward all signals to the process") 682 openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN") 683 ) 684 685 cmd.Require(flag.Min, 1) 686 utils.ParseFlags(cmd, args, true) 687 688 hijacked := make(chan io.Closer) 689 690 if *attach || *openStdin { 691 if cmd.NArg() > 1 { 692 return fmt.Errorf("You cannot start and attach multiple containers at once.") 693 } 694 695 stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) 696 if err != nil { 697 return err 698 } 699 700 env := engine.Env{} 701 if err := env.Decode(stream); err != nil { 702 return err 703 } 704 config := env.GetSubEnv("Config") 705 tty = config.GetBool("Tty") 706 707 if !tty { 708 sigc := cli.forwardAllSignals(cmd.Arg(0)) 709 defer signal.StopCatch(sigc) 710 } 711 712 var in io.ReadCloser 713 714 v := url.Values{} 715 v.Set("stream", "1") 716 717 if *openStdin && config.GetBool("OpenStdin") { 718 v.Set("stdin", "1") 719 in = cli.in 720 } 721 722 v.Set("stdout", "1") 723 v.Set("stderr", "1") 724 725 cErr = promise.Go(func() error { 726 return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil) 727 }) 728 } else { 729 close(hijacked) 730 } 731 732 // Acknowledge the hijack before starting 733 select { 734 case closer := <-hijacked: 735 // Make sure that the hijack gets closed when returning (results 736 // in closing the hijack chan and freeing server's goroutines) 737 if closer != nil { 738 defer closer.Close() 739 } 740 case err := <-cErr: 741 if err != nil { 742 return err 743 } 744 } 745 746 var encounteredError error 747 for _, name := range cmd.Args() { 748 _, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false)) 749 if err != nil { 750 if !*attach && !*openStdin { 751 fmt.Fprintf(cli.err, "%s\n", err) 752 } 753 encounteredError = fmt.Errorf("Error: failed to start one or more containers") 754 } else { 755 if !*attach && !*openStdin { 756 fmt.Fprintf(cli.out, "%s\n", name) 757 } 758 } 759 } 760 if encounteredError != nil { 761 if *openStdin || *attach { 762 cli.in.Close() 763 } 764 return encounteredError 765 } 766 767 if *openStdin || *attach { 768 if tty && cli.isTerminalOut { 769 if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { 770 log.Errorf("Error monitoring TTY size: %s", err) 771 } 772 } 773 if attchErr := <-cErr; attchErr != nil { 774 return attchErr 775 } 776 _, status, err := getExitCode(cli, cmd.Arg(0)) 777 if err != nil { 778 return err 779 } 780 if status != 0 { 781 return &utils.StatusError{StatusCode: status} 782 } 783 } 784 return nil 785 } 786 787 func (cli *DockerCli) CmdUnpause(args ...string) error { 788 cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container", true) 789 cmd.Require(flag.Exact, 1) 790 utils.ParseFlags(cmd, args, false) 791 792 var encounteredError error 793 for _, name := range cmd.Args() { 794 if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil { 795 fmt.Fprintf(cli.err, "%s\n", err) 796 encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name) 797 } else { 798 fmt.Fprintf(cli.out, "%s\n", name) 799 } 800 } 801 return encounteredError 802 } 803 804 func (cli *DockerCli) CmdPause(args ...string) error { 805 cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container", true) 806 cmd.Require(flag.Exact, 1) 807 utils.ParseFlags(cmd, args, false) 808 809 var encounteredError error 810 for _, name := range cmd.Args() { 811 if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil { 812 fmt.Fprintf(cli.err, "%s\n", err) 813 encounteredError = fmt.Errorf("Error: failed to pause container named %s", name) 814 } else { 815 fmt.Fprintf(cli.out, "%s\n", name) 816 } 817 } 818 return encounteredError 819 } 820 821 func (cli *DockerCli) CmdRename(args ...string) error { 822 cmd := cli.Subcmd("rename", "OLD_NAME NEW_NAME", "Rename a container", true) 823 if err := cmd.Parse(args); err != nil { 824 return nil 825 } 826 827 if cmd.NArg() != 2 { 828 cmd.Usage() 829 return nil 830 } 831 old_name := cmd.Arg(0) 832 new_name := cmd.Arg(1) 833 834 if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, false)); err != nil { 835 fmt.Fprintf(cli.err, "%s\n", err) 836 return fmt.Errorf("Error: failed to rename container named %s", old_name) 837 } 838 return nil 839 } 840 841 func (cli *DockerCli) CmdInspect(args ...string) error { 842 cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true) 843 tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.") 844 cmd.Require(flag.Min, 1) 845 846 utils.ParseFlags(cmd, args, true) 847 848 var tmpl *template.Template 849 if *tmplStr != "" { 850 var err error 851 if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil { 852 fmt.Fprintf(cli.err, "Template parsing error: %v\n", err) 853 return &utils.StatusError{StatusCode: 64, 854 Status: "Template parsing error: " + err.Error()} 855 } 856 } 857 858 indented := new(bytes.Buffer) 859 indented.WriteByte('[') 860 status := 0 861 862 for _, name := range cmd.Args() { 863 obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false)) 864 if err != nil { 865 obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false)) 866 if err != nil { 867 if strings.Contains(err.Error(), "No such") { 868 fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name) 869 } else { 870 fmt.Fprintf(cli.err, "%s", err) 871 } 872 status = 1 873 continue 874 } 875 } 876 877 if tmpl == nil { 878 if err = json.Indent(indented, obj, "", " "); err != nil { 879 fmt.Fprintf(cli.err, "%s\n", err) 880 status = 1 881 continue 882 } 883 } else { 884 // Has template, will render 885 var value interface{} 886 if err := json.Unmarshal(obj, &value); err != nil { 887 fmt.Fprintf(cli.err, "%s\n", err) 888 status = 1 889 continue 890 } 891 if err := tmpl.Execute(cli.out, value); err != nil { 892 return err 893 } 894 cli.out.Write([]byte{'\n'}) 895 } 896 indented.WriteString(",") 897 } 898 899 if indented.Len() > 1 { 900 // Remove trailing ',' 901 indented.Truncate(indented.Len() - 1) 902 } 903 indented.WriteString("]\n") 904 905 if tmpl == nil { 906 if _, err := io.Copy(cli.out, indented); err != nil { 907 return err 908 } 909 } 910 911 if status != 0 { 912 return &utils.StatusError{StatusCode: status} 913 } 914 return nil 915 } 916 917 func (cli *DockerCli) CmdTop(args ...string) error { 918 cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container", true) 919 cmd.Require(flag.Min, 1) 920 921 utils.ParseFlags(cmd, args, true) 922 923 val := url.Values{} 924 if cmd.NArg() > 1 { 925 val.Set("ps_args", strings.Join(cmd.Args()[1:], " ")) 926 } 927 928 stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false) 929 if err != nil { 930 return err 931 } 932 var procs engine.Env 933 if err := procs.Decode(stream); err != nil { 934 return err 935 } 936 w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 937 fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t")) 938 processes := [][]string{} 939 if err := procs.GetJson("Processes", &processes); err != nil { 940 return err 941 } 942 for _, proc := range processes { 943 fmt.Fprintln(w, strings.Join(proc, "\t")) 944 } 945 w.Flush() 946 return nil 947 } 948 949 func (cli *DockerCli) CmdPort(args ...string) error { 950 cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT", true) 951 cmd.Require(flag.Min, 1) 952 utils.ParseFlags(cmd, args, true) 953 954 stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) 955 if err != nil { 956 return err 957 } 958 959 env := engine.Env{} 960 if err := env.Decode(stream); err != nil { 961 return err 962 } 963 ports := nat.PortMap{} 964 if err := env.GetSubEnv("NetworkSettings").GetJson("Ports", &ports); err != nil { 965 return err 966 } 967 968 if cmd.NArg() == 2 { 969 var ( 970 port = cmd.Arg(1) 971 proto = "tcp" 972 parts = strings.SplitN(port, "/", 2) 973 ) 974 975 if len(parts) == 2 && len(parts[1]) != 0 { 976 port = parts[0] 977 proto = parts[1] 978 } 979 natPort := port + "/" + proto 980 if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { 981 for _, frontend := range frontends { 982 fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) 983 } 984 return nil 985 } 986 return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0)) 987 } 988 989 for from, frontends := range ports { 990 for _, frontend := range frontends { 991 fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort) 992 } 993 } 994 995 return nil 996 } 997 998 // 'docker rmi IMAGE' removes all images with the name IMAGE 999 func (cli *DockerCli) CmdRmi(args ...string) error { 1000 var ( 1001 cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images", true) 1002 force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image") 1003 noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents") 1004 ) 1005 cmd.Require(flag.Min, 1) 1006 1007 utils.ParseFlags(cmd, args, true) 1008 1009 v := url.Values{} 1010 if *force { 1011 v.Set("force", "1") 1012 } 1013 if *noprune { 1014 v.Set("noprune", "1") 1015 } 1016 1017 var encounteredError error 1018 for _, name := range cmd.Args() { 1019 body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false)) 1020 if err != nil { 1021 fmt.Fprintf(cli.err, "%s\n", err) 1022 encounteredError = fmt.Errorf("Error: failed to remove one or more images") 1023 } else { 1024 outs := engine.NewTable("Created", 0) 1025 if _, err := outs.ReadListFrom(body); err != nil { 1026 fmt.Fprintf(cli.err, "%s\n", err) 1027 encounteredError = fmt.Errorf("Error: failed to remove one or more images") 1028 continue 1029 } 1030 for _, out := range outs.Data { 1031 if out.Get("Deleted") != "" { 1032 fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted")) 1033 } else { 1034 fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged")) 1035 } 1036 } 1037 } 1038 } 1039 return encounteredError 1040 } 1041 1042 func (cli *DockerCli) CmdHistory(args ...string) error { 1043 cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true) 1044 quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") 1045 noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") 1046 cmd.Require(flag.Exact, 1) 1047 1048 utils.ParseFlags(cmd, args, true) 1049 1050 body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false)) 1051 if err != nil { 1052 return err 1053 } 1054 1055 outs := engine.NewTable("Created", 0) 1056 if _, err := outs.ReadListFrom(body); err != nil { 1057 return err 1058 } 1059 1060 w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 1061 if !*quiet { 1062 fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE") 1063 } 1064 1065 for _, out := range outs.Data { 1066 outID := out.Get("Id") 1067 if !*quiet { 1068 if *noTrunc { 1069 fmt.Fprintf(w, "%s\t", outID) 1070 } else { 1071 fmt.Fprintf(w, "%s\t", utils.TruncateID(outID)) 1072 } 1073 1074 fmt.Fprintf(w, "%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0)))) 1075 1076 if *noTrunc { 1077 fmt.Fprintf(w, "%s\t", out.Get("CreatedBy")) 1078 } else { 1079 fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45)) 1080 } 1081 fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("Size")))) 1082 } else { 1083 if *noTrunc { 1084 fmt.Fprintln(w, outID) 1085 } else { 1086 fmt.Fprintln(w, utils.TruncateID(outID)) 1087 } 1088 } 1089 } 1090 w.Flush() 1091 return nil 1092 } 1093 1094 func (cli *DockerCli) CmdRm(args ...string) error { 1095 cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers", true) 1096 v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container") 1097 link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container") 1098 force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)") 1099 cmd.Require(flag.Min, 1) 1100 1101 utils.ParseFlags(cmd, args, true) 1102 1103 val := url.Values{} 1104 if *v { 1105 val.Set("v", "1") 1106 } 1107 if *link { 1108 val.Set("link", "1") 1109 } 1110 1111 if *force { 1112 val.Set("force", "1") 1113 } 1114 1115 var encounteredError error 1116 for _, name := range cmd.Args() { 1117 _, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false)) 1118 if err != nil { 1119 fmt.Fprintf(cli.err, "%s\n", err) 1120 encounteredError = fmt.Errorf("Error: failed to remove one or more containers") 1121 } else { 1122 fmt.Fprintf(cli.out, "%s\n", name) 1123 } 1124 } 1125 return encounteredError 1126 } 1127 1128 // 'docker kill NAME' kills a running container 1129 func (cli *DockerCli) CmdKill(args ...string) error { 1130 cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal", true) 1131 signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container") 1132 cmd.Require(flag.Min, 1) 1133 1134 utils.ParseFlags(cmd, args, true) 1135 1136 var encounteredError error 1137 for _, name := range cmd.Args() { 1138 if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil { 1139 fmt.Fprintf(cli.err, "%s\n", err) 1140 encounteredError = fmt.Errorf("Error: failed to kill one or more containers") 1141 } else { 1142 fmt.Fprintf(cli.out, "%s\n", name) 1143 } 1144 } 1145 return encounteredError 1146 } 1147 1148 func (cli *DockerCli) CmdImport(args ...string) error { 1149 cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.", true) 1150 cmd.Require(flag.Min, 1) 1151 1152 utils.ParseFlags(cmd, args, true) 1153 1154 var ( 1155 v = url.Values{} 1156 src = cmd.Arg(0) 1157 repository = cmd.Arg(1) 1158 ) 1159 1160 v.Set("fromSrc", src) 1161 v.Set("repo", repository) 1162 1163 if cmd.NArg() == 3 { 1164 fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' has been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n") 1165 v.Set("tag", cmd.Arg(2)) 1166 } 1167 1168 if repository != "" { 1169 //Check if the given image name can be resolved 1170 repo, _ := parsers.ParseRepositoryTag(repository) 1171 if err := registry.ValidateRepositoryName(repo); err != nil { 1172 return err 1173 } 1174 } 1175 1176 var in io.Reader 1177 1178 if src == "-" { 1179 in = cli.in 1180 } 1181 1182 return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil) 1183 } 1184 1185 func (cli *DockerCli) CmdPush(args ...string) error { 1186 cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry", true) 1187 cmd.Require(flag.Exact, 1) 1188 1189 utils.ParseFlags(cmd, args, true) 1190 1191 name := cmd.Arg(0) 1192 1193 cli.LoadConfigFile() 1194 1195 remote, tag := parsers.ParseRepositoryTag(name) 1196 1197 // Resolve the Repository name from fqn to RepositoryInfo 1198 repoInfo, err := registry.ParseRepositoryInfo(remote) 1199 if err != nil { 1200 return err 1201 } 1202 // Resolve the Auth config relevant for this server 1203 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 1204 // If we're not using a custom registry, we know the restrictions 1205 // applied to repository names and can warn the user in advance. 1206 // Custom repositories can have different rules, and we must also 1207 // allow pushing by image ID. 1208 if repoInfo.Official { 1209 username := authConfig.Username 1210 if username == "" { 1211 username = "<user>" 1212 } 1213 return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to <user>/<repo> (ex: %s/%s)", username, repoInfo.LocalName) 1214 } 1215 1216 v := url.Values{} 1217 v.Set("tag", tag) 1218 1219 push := func(authConfig registry.AuthConfig) error { 1220 buf, err := json.Marshal(authConfig) 1221 if err != nil { 1222 return err 1223 } 1224 registryAuthHeader := []string{ 1225 base64.URLEncoding.EncodeToString(buf), 1226 } 1227 1228 return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{ 1229 "X-Registry-Auth": registryAuthHeader, 1230 }) 1231 } 1232 1233 if err := push(authConfig); err != nil { 1234 if strings.Contains(err.Error(), "Status 401") { 1235 fmt.Fprintln(cli.out, "\nPlease login prior to push:") 1236 if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil { 1237 return err 1238 } 1239 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 1240 return push(authConfig) 1241 } 1242 return err 1243 } 1244 return nil 1245 } 1246 1247 func (cli *DockerCli) CmdPull(args ...string) error { 1248 cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry", true) 1249 allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") 1250 cmd.Require(flag.Exact, 1) 1251 1252 utils.ParseFlags(cmd, args, true) 1253 1254 var ( 1255 v = url.Values{} 1256 remote = cmd.Arg(0) 1257 newRemote = remote 1258 ) 1259 taglessRemote, tag := parsers.ParseRepositoryTag(remote) 1260 if tag == "" && !*allTags { 1261 newRemote = taglessRemote + ":" + graph.DEFAULTTAG 1262 } 1263 if tag != "" && *allTags { 1264 return fmt.Errorf("tag can't be used with --all-tags/-a") 1265 } 1266 1267 v.Set("fromImage", newRemote) 1268 1269 // Resolve the Repository name from fqn to RepositoryInfo 1270 repoInfo, err := registry.ParseRepositoryInfo(taglessRemote) 1271 if err != nil { 1272 return err 1273 } 1274 1275 cli.LoadConfigFile() 1276 1277 // Resolve the Auth config relevant for this server 1278 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 1279 1280 pull := func(authConfig registry.AuthConfig) error { 1281 buf, err := json.Marshal(authConfig) 1282 if err != nil { 1283 return err 1284 } 1285 registryAuthHeader := []string{ 1286 base64.URLEncoding.EncodeToString(buf), 1287 } 1288 1289 return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{ 1290 "X-Registry-Auth": registryAuthHeader, 1291 }) 1292 } 1293 1294 if err := pull(authConfig); err != nil { 1295 if strings.Contains(err.Error(), "Status 401") { 1296 fmt.Fprintln(cli.out, "\nPlease login prior to pull:") 1297 if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil { 1298 return err 1299 } 1300 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 1301 return pull(authConfig) 1302 } 1303 return err 1304 } 1305 1306 return nil 1307 } 1308 1309 func (cli *DockerCli) CmdImages(args ...string) error { 1310 cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true) 1311 quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") 1312 all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate image layers)") 1313 noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") 1314 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1315 flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format") 1316 flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format") 1317 1318 flFilter := opts.NewListOpts(nil) 1319 cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e., 'dangling=true')") 1320 cmd.Require(flag.Max, 1) 1321 1322 utils.ParseFlags(cmd, args, true) 1323 1324 // Consolidate all filter flags, and sanity check them early. 1325 // They'll get process in the daemon/server. 1326 imageFilterArgs := filters.Args{} 1327 for _, f := range flFilter.GetAll() { 1328 var err error 1329 imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs) 1330 if err != nil { 1331 return err 1332 } 1333 } 1334 1335 matchName := cmd.Arg(0) 1336 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1337 if *flViz || *flTree { 1338 v := url.Values{ 1339 "all": []string{"1"}, 1340 } 1341 if len(imageFilterArgs) > 0 { 1342 filterJson, err := filters.ToParam(imageFilterArgs) 1343 if err != nil { 1344 return err 1345 } 1346 v.Set("filters", filterJson) 1347 } 1348 1349 body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false)) 1350 if err != nil { 1351 return err 1352 } 1353 1354 outs := engine.NewTable("Created", 0) 1355 if _, err := outs.ReadListFrom(body); err != nil { 1356 return err 1357 } 1358 1359 var ( 1360 printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string) 1361 startImage *engine.Env 1362 1363 roots = engine.NewTable("Created", outs.Len()) 1364 byParent = make(map[string]*engine.Table) 1365 ) 1366 1367 for _, image := range outs.Data { 1368 if image.Get("ParentId") == "" { 1369 roots.Add(image) 1370 } else { 1371 if children, exists := byParent[image.Get("ParentId")]; exists { 1372 children.Add(image) 1373 } else { 1374 byParent[image.Get("ParentId")] = engine.NewTable("Created", 1) 1375 byParent[image.Get("ParentId")].Add(image) 1376 } 1377 } 1378 1379 if matchName != "" { 1380 if matchName == image.Get("Id") || matchName == utils.TruncateID(image.Get("Id")) { 1381 startImage = image 1382 } 1383 1384 for _, repotag := range image.GetList("RepoTags") { 1385 if repotag == matchName { 1386 startImage = image 1387 } 1388 } 1389 } 1390 } 1391 1392 if *flViz { 1393 fmt.Fprintf(cli.out, "digraph docker {\n") 1394 printNode = (*DockerCli).printVizNode 1395 } else { 1396 printNode = (*DockerCli).printTreeNode 1397 } 1398 1399 if startImage != nil { 1400 root := engine.NewTable("Created", 1) 1401 root.Add(startImage) 1402 cli.WalkTree(*noTrunc, root, byParent, "", printNode) 1403 } else if matchName == "" { 1404 cli.WalkTree(*noTrunc, roots, byParent, "", printNode) 1405 } 1406 if *flViz { 1407 fmt.Fprintf(cli.out, " base [style=invisible]\n}\n") 1408 } 1409 } else { 1410 v := url.Values{} 1411 if len(imageFilterArgs) > 0 { 1412 filterJson, err := filters.ToParam(imageFilterArgs) 1413 if err != nil { 1414 return err 1415 } 1416 v.Set("filters", filterJson) 1417 } 1418 1419 if cmd.NArg() == 1 { 1420 // FIXME rename this parameter, to not be confused with the filters flag 1421 v.Set("filter", matchName) 1422 } 1423 if *all { 1424 v.Set("all", "1") 1425 } 1426 1427 body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false)) 1428 1429 if err != nil { 1430 return err 1431 } 1432 1433 outs := engine.NewTable("Created", 0) 1434 if _, err := outs.ReadListFrom(body); err != nil { 1435 return err 1436 } 1437 1438 w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 1439 if !*quiet { 1440 fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE") 1441 } 1442 1443 for _, out := range outs.Data { 1444 for _, repotag := range out.GetList("RepoTags") { 1445 1446 repo, tag := parsers.ParseRepositoryTag(repotag) 1447 outID := out.Get("Id") 1448 if !*noTrunc { 1449 outID = utils.TruncateID(outID) 1450 } 1451 1452 if !*quiet { 1453 fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize")))) 1454 } else { 1455 fmt.Fprintln(w, outID) 1456 } 1457 } 1458 } 1459 1460 if !*quiet { 1461 w.Flush() 1462 } 1463 } 1464 return nil 1465 } 1466 1467 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1468 func (cli *DockerCli) WalkTree(noTrunc bool, images *engine.Table, byParent map[string]*engine.Table, prefix string, printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)) { 1469 length := images.Len() 1470 if length > 1 { 1471 for index, image := range images.Data { 1472 if index+1 == length { 1473 printNode(cli, noTrunc, image, prefix+"└─") 1474 if subimages, exists := byParent[image.Get("Id")]; exists { 1475 cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode) 1476 } 1477 } else { 1478 printNode(cli, noTrunc, image, prefix+"\u251C─") 1479 if subimages, exists := byParent[image.Get("Id")]; exists { 1480 cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode) 1481 } 1482 } 1483 } 1484 } else { 1485 for _, image := range images.Data { 1486 printNode(cli, noTrunc, image, prefix+"└─") 1487 if subimages, exists := byParent[image.Get("Id")]; exists { 1488 cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode) 1489 } 1490 } 1491 } 1492 } 1493 1494 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1495 func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) { 1496 var ( 1497 imageID string 1498 parentID string 1499 ) 1500 if noTrunc { 1501 imageID = image.Get("Id") 1502 parentID = image.Get("ParentId") 1503 } else { 1504 imageID = utils.TruncateID(image.Get("Id")) 1505 parentID = utils.TruncateID(image.Get("ParentId")) 1506 } 1507 if parentID == "" { 1508 fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID) 1509 } else { 1510 fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID) 1511 } 1512 if image.GetList("RepoTags")[0] != "<none>:<none>" { 1513 fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", 1514 imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n")) 1515 } 1516 } 1517 1518 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1519 func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) { 1520 var imageID string 1521 if noTrunc { 1522 imageID = image.Get("Id") 1523 } else { 1524 imageID = utils.TruncateID(image.Get("Id")) 1525 } 1526 1527 fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.GetInt64("VirtualSize")))) 1528 if image.GetList("RepoTags")[0] != "<none>:<none>" { 1529 fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", ")) 1530 } else { 1531 fmt.Fprint(cli.out, "\n") 1532 } 1533 } 1534 1535 func (cli *DockerCli) CmdPs(args ...string) error { 1536 var ( 1537 err error 1538 1539 psFilterArgs = filters.Args{} 1540 v = url.Values{} 1541 1542 cmd = cli.Subcmd("ps", "", "List containers", true) 1543 quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") 1544 size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes") 1545 all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") 1546 noTrunc = cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") 1547 nLatest = cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.") 1548 since = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.") 1549 before = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.") 1550 last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.") 1551 flFilter = opts.NewListOpts(nil) 1552 ) 1553 cmd.Require(flag.Exact, 0) 1554 1555 cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>\nstatus=(restarting|running|paused|exited)") 1556 1557 utils.ParseFlags(cmd, args, true) 1558 if *last == -1 && *nLatest { 1559 *last = 1 1560 } 1561 1562 if *all { 1563 v.Set("all", "1") 1564 } 1565 1566 if *last != -1 { 1567 v.Set("limit", strconv.Itoa(*last)) 1568 } 1569 1570 if *since != "" { 1571 v.Set("since", *since) 1572 } 1573 1574 if *before != "" { 1575 v.Set("before", *before) 1576 } 1577 1578 if *size { 1579 v.Set("size", "1") 1580 } 1581 1582 // Consolidate all filter flags, and sanity check them. 1583 // They'll get processed in the daemon/server. 1584 for _, f := range flFilter.GetAll() { 1585 if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil { 1586 return err 1587 } 1588 } 1589 1590 if len(psFilterArgs) > 0 { 1591 filterJson, err := filters.ToParam(psFilterArgs) 1592 if err != nil { 1593 return err 1594 } 1595 1596 v.Set("filters", filterJson) 1597 } 1598 1599 body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false)) 1600 if err != nil { 1601 return err 1602 } 1603 1604 outs := engine.NewTable("Created", 0) 1605 if _, err := outs.ReadListFrom(body); err != nil { 1606 return err 1607 } 1608 1609 w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 1610 if !*quiet { 1611 fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") 1612 1613 if *size { 1614 fmt.Fprintln(w, "\tSIZE") 1615 } else { 1616 fmt.Fprint(w, "\n") 1617 } 1618 } 1619 1620 stripNamePrefix := func(ss []string) []string { 1621 for i, s := range ss { 1622 ss[i] = s[1:] 1623 } 1624 1625 return ss 1626 } 1627 1628 for _, out := range outs.Data { 1629 outID := out.Get("Id") 1630 1631 if !*noTrunc { 1632 outID = utils.TruncateID(outID) 1633 } 1634 1635 if *quiet { 1636 fmt.Fprintln(w, outID) 1637 1638 continue 1639 } 1640 1641 var ( 1642 outNames = stripNamePrefix(out.GetList("Names")) 1643 outCommand = strconv.Quote(out.Get("Command")) 1644 ports = engine.NewTable("", 0) 1645 ) 1646 1647 if !*noTrunc { 1648 outCommand = utils.Trunc(outCommand, 20) 1649 1650 // only display the default name for the container with notrunc is passed 1651 for _, name := range outNames { 1652 if len(strings.Split(name, "/")) == 1 { 1653 outNames = []string{name} 1654 1655 break 1656 } 1657 } 1658 } 1659 1660 ports.ReadListFrom([]byte(out.Get("Ports"))) 1661 1662 image := out.Get("Image") 1663 if image == "" { 1664 image = "<no image>" 1665 } 1666 1667 fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand, 1668 units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), 1669 out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ",")) 1670 1671 if *size { 1672 if out.GetInt("SizeRootFs") > 0 { 1673 fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(out.GetInt64("SizeRw"))), units.HumanSize(float64(out.GetInt64("SizeRootFs")))) 1674 } else { 1675 fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("SizeRw")))) 1676 } 1677 1678 continue 1679 } 1680 1681 fmt.Fprint(w, "\n") 1682 } 1683 1684 if !*quiet { 1685 w.Flush() 1686 } 1687 1688 return nil 1689 } 1690 1691 func (cli *DockerCli) CmdCommit(args ...string) error { 1692 cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes", true) 1693 flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit") 1694 flComment := cmd.String([]string{"m", "-message"}, "", "Commit message") 1695 flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")") 1696 // FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands. 1697 flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands") 1698 cmd.Require(flag.Max, 2) 1699 cmd.Require(flag.Min, 1) 1700 utils.ParseFlags(cmd, args, true) 1701 1702 var ( 1703 name = cmd.Arg(0) 1704 repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) 1705 ) 1706 1707 //Check if the given image name can be resolved 1708 if repository != "" { 1709 if err := registry.ValidateRepositoryName(repository); err != nil { 1710 return err 1711 } 1712 } 1713 1714 v := url.Values{} 1715 v.Set("container", name) 1716 v.Set("repo", repository) 1717 v.Set("tag", tag) 1718 v.Set("comment", *flComment) 1719 v.Set("author", *flAuthor) 1720 1721 if *flPause != true { 1722 v.Set("pause", "0") 1723 } 1724 1725 var ( 1726 config *runconfig.Config 1727 env engine.Env 1728 ) 1729 if *flConfig != "" { 1730 config = &runconfig.Config{} 1731 if err := json.Unmarshal([]byte(*flConfig), config); err != nil { 1732 return err 1733 } 1734 } 1735 stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false) 1736 if err != nil { 1737 return err 1738 } 1739 if err := env.Decode(stream); err != nil { 1740 return err 1741 } 1742 1743 fmt.Fprintf(cli.out, "%s\n", env.Get("Id")) 1744 return nil 1745 } 1746 1747 func (cli *DockerCli) CmdEvents(args ...string) error { 1748 cmd := cli.Subcmd("events", "", "Get real time events from the server", true) 1749 since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp") 1750 until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") 1751 flFilter := opts.NewListOpts(nil) 1752 cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e., 'event=stop')") 1753 cmd.Require(flag.Exact, 0) 1754 1755 utils.ParseFlags(cmd, args, true) 1756 1757 var ( 1758 v = url.Values{} 1759 loc = time.FixedZone(time.Now().Zone()) 1760 eventFilterArgs = filters.Args{} 1761 ) 1762 1763 // Consolidate all filter flags, and sanity check them early. 1764 // They'll get process in the daemon/server. 1765 for _, f := range flFilter.GetAll() { 1766 var err error 1767 eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs) 1768 if err != nil { 1769 return err 1770 } 1771 } 1772 var setTime = func(key, value string) { 1773 format := timeutils.RFC3339NanoFixed 1774 if len(value) < len(format) { 1775 format = format[:len(value)] 1776 } 1777 if t, err := time.ParseInLocation(format, value, loc); err == nil { 1778 v.Set(key, strconv.FormatInt(t.Unix(), 10)) 1779 } else { 1780 v.Set(key, value) 1781 } 1782 } 1783 if *since != "" { 1784 setTime("since", *since) 1785 } 1786 if *until != "" { 1787 setTime("until", *until) 1788 } 1789 if len(eventFilterArgs) > 0 { 1790 filterJson, err := filters.ToParam(eventFilterArgs) 1791 if err != nil { 1792 return err 1793 } 1794 v.Set("filters", filterJson) 1795 } 1796 if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil { 1797 return err 1798 } 1799 return nil 1800 } 1801 1802 func (cli *DockerCli) CmdExport(args ...string) error { 1803 cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT", true) 1804 cmd.Require(flag.Exact, 1) 1805 1806 utils.ParseFlags(cmd, args, true) 1807 1808 if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil { 1809 return err 1810 } 1811 return nil 1812 } 1813 1814 func (cli *DockerCli) CmdDiff(args ...string) error { 1815 cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem", true) 1816 cmd.Require(flag.Exact, 1) 1817 1818 utils.ParseFlags(cmd, args, true) 1819 1820 body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false)) 1821 1822 if err != nil { 1823 return err 1824 } 1825 1826 outs := engine.NewTable("", 0) 1827 if _, err := outs.ReadListFrom(body); err != nil { 1828 return err 1829 } 1830 for _, change := range outs.Data { 1831 var kind string 1832 switch change.GetInt("Kind") { 1833 case archive.ChangeModify: 1834 kind = "C" 1835 case archive.ChangeAdd: 1836 kind = "A" 1837 case archive.ChangeDelete: 1838 kind = "D" 1839 } 1840 fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path")) 1841 } 1842 return nil 1843 } 1844 1845 func (cli *DockerCli) CmdLogs(args ...string) error { 1846 var ( 1847 cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container", true) 1848 follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output") 1849 times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps") 1850 tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)") 1851 ) 1852 cmd.Require(flag.Exact, 1) 1853 1854 utils.ParseFlags(cmd, args, true) 1855 1856 name := cmd.Arg(0) 1857 1858 stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) 1859 if err != nil { 1860 return err 1861 } 1862 1863 env := engine.Env{} 1864 if err := env.Decode(stream); err != nil { 1865 return err 1866 } 1867 1868 v := url.Values{} 1869 v.Set("stdout", "1") 1870 v.Set("stderr", "1") 1871 1872 if *times { 1873 v.Set("timestamps", "1") 1874 } 1875 1876 if *follow { 1877 v.Set("follow", "1") 1878 } 1879 v.Set("tail", *tail) 1880 1881 return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil) 1882 } 1883 1884 func (cli *DockerCli) CmdAttach(args ...string) error { 1885 var ( 1886 cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container", true) 1887 noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN") 1888 proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process (non-TTY mode only). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.") 1889 ) 1890 cmd.Require(flag.Exact, 1) 1891 1892 utils.ParseFlags(cmd, args, true) 1893 name := cmd.Arg(0) 1894 1895 stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) 1896 if err != nil { 1897 return err 1898 } 1899 1900 env := engine.Env{} 1901 if err := env.Decode(stream); err != nil { 1902 return err 1903 } 1904 1905 if !env.GetSubEnv("State").GetBool("Running") { 1906 return fmt.Errorf("You cannot attach to a stopped container, start it first") 1907 } 1908 1909 var ( 1910 config = env.GetSubEnv("Config") 1911 tty = config.GetBool("Tty") 1912 ) 1913 1914 if err := cli.CheckTtyInput(!*noStdin, tty); err != nil { 1915 return err 1916 } 1917 1918 if tty && cli.isTerminalOut { 1919 if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { 1920 log.Debugf("Error monitoring TTY size: %s", err) 1921 } 1922 } 1923 1924 var in io.ReadCloser 1925 1926 v := url.Values{} 1927 v.Set("stream", "1") 1928 if !*noStdin && config.GetBool("OpenStdin") { 1929 v.Set("stdin", "1") 1930 in = cli.in 1931 } 1932 1933 v.Set("stdout", "1") 1934 v.Set("stderr", "1") 1935 1936 if *proxy && !tty { 1937 sigc := cli.forwardAllSignals(cmd.Arg(0)) 1938 defer signal.StopCatch(sigc) 1939 } 1940 1941 if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil); err != nil { 1942 return err 1943 } 1944 1945 _, status, err := getExitCode(cli, cmd.Arg(0)) 1946 if err != nil { 1947 return err 1948 } 1949 if status != 0 { 1950 return &utils.StatusError{StatusCode: status} 1951 } 1952 1953 return nil 1954 } 1955 1956 func (cli *DockerCli) CmdSearch(args ...string) error { 1957 cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images", true) 1958 noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") 1959 trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds") 1960 automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds") 1961 stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars") 1962 cmd.Require(flag.Exact, 1) 1963 1964 utils.ParseFlags(cmd, args, true) 1965 1966 v := url.Values{} 1967 v.Set("term", cmd.Arg(0)) 1968 1969 body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true)) 1970 1971 if err != nil { 1972 return err 1973 } 1974 outs := engine.NewTable("star_count", 0) 1975 if _, err := outs.ReadListFrom(body); err != nil { 1976 return err 1977 } 1978 w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0) 1979 fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n") 1980 for _, out := range outs.Data { 1981 if ((*automated || *trusted) && (!out.GetBool("is_trusted") && !out.GetBool("is_automated"))) || (*stars > out.GetInt("star_count")) { 1982 continue 1983 } 1984 desc := strings.Replace(out.Get("description"), "\n", " ", -1) 1985 desc = strings.Replace(desc, "\r", " ", -1) 1986 if !*noTrunc && len(desc) > 45 { 1987 desc = utils.Trunc(desc, 42) + "..." 1988 } 1989 fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count")) 1990 if out.GetBool("is_official") { 1991 fmt.Fprint(w, "[OK]") 1992 1993 } 1994 fmt.Fprint(w, "\t") 1995 if out.GetBool("is_automated") || out.GetBool("is_trusted") { 1996 fmt.Fprint(w, "[OK]") 1997 } 1998 fmt.Fprint(w, "\n") 1999 } 2000 w.Flush() 2001 return nil 2002 } 2003 2004 // Ports type - Used to parse multiple -p flags 2005 type ports []int 2006 2007 func (cli *DockerCli) CmdTag(args ...string) error { 2008 cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository", true) 2009 force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") 2010 cmd.Require(flag.Exact, 2) 2011 2012 utils.ParseFlags(cmd, args, true) 2013 2014 var ( 2015 repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) 2016 v = url.Values{} 2017 ) 2018 2019 //Check if the given image name can be resolved 2020 if err := registry.ValidateRepositoryName(repository); err != nil { 2021 return err 2022 } 2023 v.Set("repo", repository) 2024 v.Set("tag", tag) 2025 2026 if *force { 2027 v.Set("force", "1") 2028 } 2029 2030 if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil { 2031 return err 2032 } 2033 return nil 2034 } 2035 2036 func (cli *DockerCli) pullImage(image string) error { 2037 return cli.pullImageCustomOut(image, cli.out) 2038 } 2039 2040 func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { 2041 v := url.Values{} 2042 repos, tag := parsers.ParseRepositoryTag(image) 2043 // pull only the image tagged 'latest' if no tag was specified 2044 if tag == "" { 2045 tag = graph.DEFAULTTAG 2046 } 2047 v.Set("fromImage", repos) 2048 v.Set("tag", tag) 2049 2050 // Resolve the Repository name from fqn to RepositoryInfo 2051 repoInfo, err := registry.ParseRepositoryInfo(repos) 2052 if err != nil { 2053 return err 2054 } 2055 2056 // Load the auth config file, to be able to pull the image 2057 cli.LoadConfigFile() 2058 2059 // Resolve the Auth config relevant for this server 2060 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 2061 buf, err := json.Marshal(authConfig) 2062 if err != nil { 2063 return err 2064 } 2065 2066 registryAuthHeader := []string{ 2067 base64.URLEncoding.EncodeToString(buf), 2068 } 2069 if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { 2070 return err 2071 } 2072 return nil 2073 } 2074 2075 type cidFile struct { 2076 path string 2077 file *os.File 2078 written bool 2079 } 2080 2081 func newCIDFile(path string) (*cidFile, error) { 2082 if _, err := os.Stat(path); err == nil { 2083 return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path) 2084 } 2085 2086 f, err := os.Create(path) 2087 if err != nil { 2088 return nil, fmt.Errorf("Failed to create the container ID file: %s", err) 2089 } 2090 2091 return &cidFile{path: path, file: f}, nil 2092 } 2093 2094 func (cid *cidFile) Close() error { 2095 cid.file.Close() 2096 2097 if !cid.written { 2098 if err := os.Remove(cid.path); err != nil { 2099 return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err) 2100 } 2101 } 2102 2103 return nil 2104 } 2105 2106 func (cid *cidFile) Write(id string) error { 2107 if _, err := cid.file.Write([]byte(id)); err != nil { 2108 return fmt.Errorf("Failed to write the container ID to the file: %s", err) 2109 } 2110 cid.written = true 2111 return nil 2112 } 2113 2114 func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) { 2115 containerValues := url.Values{} 2116 if name != "" { 2117 containerValues.Set("name", name) 2118 } 2119 2120 mergedConfig := runconfig.MergeConfigs(config, hostConfig) 2121 2122 var containerIDFile *cidFile 2123 if cidfile != "" { 2124 var err error 2125 if containerIDFile, err = newCIDFile(cidfile); err != nil { 2126 return nil, err 2127 } 2128 defer containerIDFile.Close() 2129 } 2130 2131 //create the container 2132 stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false) 2133 //if image not found try to pull it 2134 if statusCode == 404 { 2135 repo, tag := parsers.ParseRepositoryTag(config.Image) 2136 if tag == "" { 2137 tag = graph.DEFAULTTAG 2138 } 2139 fmt.Fprintf(cli.err, "Unable to find image '%s:%s' locally\n", repo, tag) 2140 2141 // we don't want to write to stdout anything apart from container.ID 2142 if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { 2143 return nil, err 2144 } 2145 // Retry 2146 if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil { 2147 return nil, err 2148 } 2149 } else if err != nil { 2150 return nil, err 2151 } 2152 2153 var result engine.Env 2154 if err := result.Decode(stream); err != nil { 2155 return nil, err 2156 } 2157 2158 for _, warning := range result.GetList("Warnings") { 2159 fmt.Fprintf(cli.err, "WARNING: %s\n", warning) 2160 } 2161 2162 if containerIDFile != nil { 2163 if err = containerIDFile.Write(result.Get("Id")); err != nil { 2164 return nil, err 2165 } 2166 } 2167 2168 return result, nil 2169 2170 } 2171 2172 func (cli *DockerCli) CmdCreate(args ...string) error { 2173 cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true) 2174 2175 // These are flags not stored in Config/HostConfig 2176 var ( 2177 flName = cmd.String([]string{"-name"}, "", "Assign a name to the container") 2178 ) 2179 2180 config, hostConfig, cmd, err := runconfig.Parse(cmd, args) 2181 if err != nil { 2182 utils.ReportError(cmd, err.Error(), true) 2183 } 2184 if config.Image == "" { 2185 cmd.Usage() 2186 return nil 2187 } 2188 2189 createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) 2190 if err != nil { 2191 return err 2192 } 2193 2194 fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id")) 2195 2196 return nil 2197 } 2198 2199 func (cli *DockerCli) CmdRun(args ...string) error { 2200 // FIXME: just use runconfig.Parse already 2201 cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container", true) 2202 2203 // These are flags not stored in Config/HostConfig 2204 var ( 2205 flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") 2206 flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run the container in the background and print the new container ID") 2207 flSigProxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.") 2208 flName = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container") 2209 flAttach *opts.ListOpts 2210 2211 ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d") 2212 ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm") 2213 ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d") 2214 ) 2215 2216 config, hostConfig, cmd, err := runconfig.Parse(cmd, args) 2217 // just in case the Parse does not exit 2218 if err != nil { 2219 utils.ReportError(cmd, err.Error(), true) 2220 } 2221 if config.Image == "" { 2222 cmd.Usage() 2223 return nil 2224 } 2225 2226 if !*flDetach { 2227 if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil { 2228 return err 2229 } 2230 } else { 2231 if fl := cmd.Lookup("attach"); fl != nil { 2232 flAttach = fl.Value.(*opts.ListOpts) 2233 if flAttach.Len() != 0 { 2234 return ErrConflictAttachDetach 2235 } 2236 } 2237 if *flAutoRemove { 2238 return ErrConflictDetachAutoRemove 2239 } 2240 2241 config.AttachStdin = false 2242 config.AttachStdout = false 2243 config.AttachStderr = false 2244 config.StdinOnce = false 2245 } 2246 2247 // Disable flSigProxy when in TTY mode 2248 sigProxy := *flSigProxy 2249 if config.Tty { 2250 sigProxy = false 2251 } 2252 2253 runResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) 2254 if err != nil { 2255 return err 2256 } 2257 2258 if sigProxy { 2259 sigc := cli.forwardAllSignals(runResult.Get("Id")) 2260 defer signal.StopCatch(sigc) 2261 } 2262 2263 var ( 2264 waitDisplayId chan struct{} 2265 errCh chan error 2266 ) 2267 2268 if !config.AttachStdout && !config.AttachStderr { 2269 // Make this asynchronous to allow the client to write to stdin before having to read the ID 2270 waitDisplayId = make(chan struct{}) 2271 go func() { 2272 defer close(waitDisplayId) 2273 fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id")) 2274 }() 2275 } 2276 2277 if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") { 2278 return ErrConflictRestartPolicyAndAutoRemove 2279 } 2280 2281 // We need to instantiate the chan because the select needs it. It can 2282 // be closed but can't be uninitialized. 2283 hijacked := make(chan io.Closer) 2284 2285 // Block the return until the chan gets closed 2286 defer func() { 2287 log.Debugf("End of CmdRun(), Waiting for hijack to finish.") 2288 if _, ok := <-hijacked; ok { 2289 log.Errorf("Hijack did not finish (chan still open)") 2290 } 2291 }() 2292 2293 if config.AttachStdin || config.AttachStdout || config.AttachStderr { 2294 var ( 2295 out, stderr io.Writer 2296 in io.ReadCloser 2297 v = url.Values{} 2298 ) 2299 v.Set("stream", "1") 2300 2301 if config.AttachStdin { 2302 v.Set("stdin", "1") 2303 in = cli.in 2304 } 2305 if config.AttachStdout { 2306 v.Set("stdout", "1") 2307 out = cli.out 2308 } 2309 if config.AttachStderr { 2310 v.Set("stderr", "1") 2311 if config.Tty { 2312 stderr = cli.out 2313 } else { 2314 stderr = cli.err 2315 } 2316 } 2317 2318 errCh = promise.Go(func() error { 2319 return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil) 2320 }) 2321 } else { 2322 close(hijacked) 2323 } 2324 2325 // Acknowledge the hijack before starting 2326 select { 2327 case closer := <-hijacked: 2328 // Make sure that the hijack gets closed when returning (results 2329 // in closing the hijack chan and freeing server's goroutines) 2330 if closer != nil { 2331 defer closer.Close() 2332 } 2333 case err := <-errCh: 2334 if err != nil { 2335 log.Debugf("Error hijack: %s", err) 2336 return err 2337 } 2338 } 2339 2340 //start the container 2341 if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", nil, false)); err != nil { 2342 return err 2343 } 2344 2345 if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { 2346 if err := cli.monitorTtySize(runResult.Get("Id"), false); err != nil { 2347 log.Errorf("Error monitoring TTY size: %s", err) 2348 } 2349 } 2350 2351 if errCh != nil { 2352 if err := <-errCh; err != nil { 2353 log.Debugf("Error hijack: %s", err) 2354 return err 2355 } 2356 } 2357 2358 // Detached mode: wait for the id to be displayed and return. 2359 if !config.AttachStdout && !config.AttachStderr { 2360 // Detached mode 2361 <-waitDisplayId 2362 return nil 2363 } 2364 2365 var status int 2366 2367 // Attached mode 2368 if *flAutoRemove { 2369 // Autoremove: wait for the container to finish, retrieve 2370 // the exit code and remove the container 2371 if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil { 2372 return err 2373 } 2374 if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { 2375 return err 2376 } 2377 if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil { 2378 return err 2379 } 2380 } else { 2381 // No Autoremove: Simply retrieve the exit code 2382 if !config.Tty { 2383 // In non-TTY mode, we can't detach, so we must wait for container exit 2384 if status, err = waitForExit(cli, runResult.Get("Id")); err != nil { 2385 return err 2386 } 2387 } else { 2388 // In TTY mode, there is a race: if the process dies too slowly, the state could 2389 // be updated after the getExitCode call and result in the wrong exit code being reported 2390 if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { 2391 return err 2392 } 2393 } 2394 } 2395 if status != 0 { 2396 return &utils.StatusError{StatusCode: status} 2397 } 2398 return nil 2399 } 2400 2401 func (cli *DockerCli) CmdCp(args ...string) error { 2402 cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH", true) 2403 cmd.Require(flag.Exact, 2) 2404 2405 utils.ParseFlags(cmd, args, true) 2406 2407 var copyData engine.Env 2408 info := strings.Split(cmd.Arg(0), ":") 2409 2410 if len(info) != 2 { 2411 return fmt.Errorf("Error: Path not specified") 2412 } 2413 2414 copyData.Set("Resource", info[1]) 2415 copyData.Set("HostPath", cmd.Arg(1)) 2416 2417 stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false) 2418 if stream != nil { 2419 defer stream.Close() 2420 } 2421 if statusCode == 404 { 2422 return fmt.Errorf("No such container: %v", info[0]) 2423 } 2424 if err != nil { 2425 return err 2426 } 2427 2428 if statusCode == 200 { 2429 if err := archive.Untar(stream, copyData.Get("HostPath"), &archive.TarOptions{NoLchown: true}); err != nil { 2430 return err 2431 } 2432 } 2433 return nil 2434 } 2435 2436 func (cli *DockerCli) CmdSave(args ...string) error { 2437 cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)", true) 2438 outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT") 2439 cmd.Require(flag.Min, 1) 2440 2441 utils.ParseFlags(cmd, args, true) 2442 2443 var ( 2444 output io.Writer = cli.out 2445 err error 2446 ) 2447 if *outfile != "" { 2448 output, err = os.Create(*outfile) 2449 if err != nil { 2450 return err 2451 } 2452 } else if cli.isTerminalOut { 2453 return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.") 2454 } 2455 2456 if len(cmd.Args()) == 1 { 2457 image := cmd.Arg(0) 2458 if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil { 2459 return err 2460 } 2461 } else { 2462 v := url.Values{} 2463 for _, arg := range cmd.Args() { 2464 v.Add("names", arg) 2465 } 2466 if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil { 2467 return err 2468 } 2469 } 2470 return nil 2471 } 2472 2473 func (cli *DockerCli) CmdLoad(args ...string) error { 2474 cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN", true) 2475 infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN") 2476 cmd.Require(flag.Exact, 0) 2477 2478 utils.ParseFlags(cmd, args, true) 2479 2480 var ( 2481 input io.Reader = cli.in 2482 err error 2483 ) 2484 if *infile != "" { 2485 input, err = os.Open(*infile) 2486 if err != nil { 2487 return err 2488 } 2489 } 2490 if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil { 2491 return err 2492 } 2493 return nil 2494 } 2495 2496 func (cli *DockerCli) CmdExec(args ...string) error { 2497 cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true) 2498 2499 execConfig, err := runconfig.ParseExec(cmd, args) 2500 // just in case the ParseExec does not exit 2501 if execConfig.Container == "" || err != nil { 2502 return &utils.StatusError{StatusCode: 1} 2503 } 2504 2505 stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false) 2506 if err != nil { 2507 return err 2508 } 2509 2510 var execResult engine.Env 2511 if err := execResult.Decode(stream); err != nil { 2512 return err 2513 } 2514 2515 execID := execResult.Get("Id") 2516 2517 if execID == "" { 2518 fmt.Fprintf(cli.out, "exec ID empty") 2519 return nil 2520 } 2521 2522 if !execConfig.Detach { 2523 if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { 2524 return err 2525 } 2526 } else { 2527 if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil { 2528 return err 2529 } 2530 // For now don't print this - wait for when we support exec wait() 2531 // fmt.Fprintf(cli.out, "%s\n", execID) 2532 return nil 2533 } 2534 2535 // Interactive exec requested. 2536 var ( 2537 out, stderr io.Writer 2538 in io.ReadCloser 2539 hijacked = make(chan io.Closer) 2540 errCh chan error 2541 ) 2542 2543 // Block the return until the chan gets closed 2544 defer func() { 2545 log.Debugf("End of CmdExec(), Waiting for hijack to finish.") 2546 if _, ok := <-hijacked; ok { 2547 log.Errorf("Hijack did not finish (chan still open)") 2548 } 2549 }() 2550 2551 if execConfig.AttachStdin { 2552 in = cli.in 2553 } 2554 if execConfig.AttachStdout { 2555 out = cli.out 2556 } 2557 if execConfig.AttachStderr { 2558 if execConfig.Tty { 2559 stderr = cli.out 2560 } else { 2561 stderr = cli.err 2562 } 2563 } 2564 errCh = promise.Go(func() error { 2565 return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig) 2566 }) 2567 2568 // Acknowledge the hijack before starting 2569 select { 2570 case closer := <-hijacked: 2571 // Make sure that hijack gets closed when returning. (result 2572 // in closing hijack chan and freeing server's goroutines. 2573 if closer != nil { 2574 defer closer.Close() 2575 } 2576 case err := <-errCh: 2577 if err != nil { 2578 log.Debugf("Error hijack: %s", err) 2579 return err 2580 } 2581 } 2582 2583 if execConfig.Tty && cli.isTerminalIn { 2584 if err := cli.monitorTtySize(execID, true); err != nil { 2585 log.Errorf("Error monitoring TTY size: %s", err) 2586 } 2587 } 2588 2589 if err := <-errCh; err != nil { 2590 log.Debugf("Error hijack: %s", err) 2591 return err 2592 } 2593 2594 var status int 2595 if _, status, err = getExecExitCode(cli, execID); err != nil { 2596 return err 2597 } 2598 2599 if status != 0 { 2600 return &utils.StatusError{StatusCode: status} 2601 } 2602 2603 return nil 2604 } 2605 2606 type containerStats struct { 2607 Name string 2608 CpuPercentage float64 2609 Memory float64 2610 MemoryLimit float64 2611 MemoryPercentage float64 2612 NetworkRx float64 2613 NetworkTx float64 2614 mu sync.RWMutex 2615 err error 2616 } 2617 2618 func (s *containerStats) Collect(cli *DockerCli) { 2619 stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false) 2620 if err != nil { 2621 s.err = err 2622 return 2623 } 2624 defer stream.Close() 2625 var ( 2626 previousCpu uint64 2627 previousSystem uint64 2628 start = true 2629 dec = json.NewDecoder(stream) 2630 u = make(chan error, 1) 2631 ) 2632 go func() { 2633 for { 2634 var v *stats.Stats 2635 if err := dec.Decode(&v); err != nil { 2636 u <- err 2637 return 2638 } 2639 var ( 2640 memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0 2641 cpuPercent = 0.0 2642 ) 2643 if !start { 2644 cpuPercent = calcuateCpuPercent(previousCpu, previousSystem, v) 2645 } 2646 start = false 2647 s.mu.Lock() 2648 s.CpuPercentage = cpuPercent 2649 s.Memory = float64(v.MemoryStats.Usage) 2650 s.MemoryLimit = float64(v.MemoryStats.Limit) 2651 s.MemoryPercentage = memPercent 2652 s.NetworkRx = float64(v.Network.RxBytes) 2653 s.NetworkTx = float64(v.Network.TxBytes) 2654 s.mu.Unlock() 2655 previousCpu = v.CpuStats.CpuUsage.TotalUsage 2656 previousSystem = v.CpuStats.SystemUsage 2657 u <- nil 2658 } 2659 }() 2660 for { 2661 select { 2662 case <-time.After(2 * time.Second): 2663 // zero out the values if we have not received an update within 2664 // the specified duration. 2665 s.mu.Lock() 2666 s.CpuPercentage = 0 2667 s.Memory = 0 2668 s.MemoryPercentage = 0 2669 s.mu.Unlock() 2670 case err := <-u: 2671 if err != nil { 2672 s.mu.Lock() 2673 s.err = err 2674 s.mu.Unlock() 2675 return 2676 } 2677 } 2678 } 2679 } 2680 2681 func (s *containerStats) Display(w io.Writer) error { 2682 s.mu.RLock() 2683 defer s.mu.RUnlock() 2684 if s.err != nil { 2685 return s.err 2686 } 2687 fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n", 2688 s.Name, 2689 s.CpuPercentage, 2690 units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit), 2691 s.MemoryPercentage, 2692 units.BytesSize(s.NetworkRx), units.BytesSize(s.NetworkTx)) 2693 return nil 2694 } 2695 2696 func (cli *DockerCli) CmdStats(args ...string) error { 2697 cmd := cli.Subcmd("stats", "CONTAINER", "Display a live stream of one or more containers' resource usage statistics", true) 2698 cmd.Require(flag.Min, 1) 2699 utils.ParseFlags(cmd, args, true) 2700 2701 names := cmd.Args() 2702 sort.Strings(names) 2703 var ( 2704 cStats []*containerStats 2705 w = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 2706 ) 2707 printHeader := func() { 2708 fmt.Fprint(cli.out, "\033[2J") 2709 fmt.Fprint(cli.out, "\033[H") 2710 fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O") 2711 } 2712 for _, n := range names { 2713 s := &containerStats{Name: n} 2714 cStats = append(cStats, s) 2715 go s.Collect(cli) 2716 } 2717 // do a quick pause so that any failed connections for containers that do not exist are able to be 2718 // evicted before we display the initial or default values. 2719 time.Sleep(500 * time.Millisecond) 2720 var errs []string 2721 for _, c := range cStats { 2722 c.mu.Lock() 2723 if c.err != nil { 2724 errs = append(errs, fmt.Sprintf("%s: %s", c.Name, c.err.Error())) 2725 } 2726 c.mu.Unlock() 2727 } 2728 if len(errs) > 0 { 2729 return fmt.Errorf("%s", strings.Join(errs, ", ")) 2730 } 2731 for _ = range time.Tick(500 * time.Millisecond) { 2732 printHeader() 2733 toRemove := []int{} 2734 for i, s := range cStats { 2735 if err := s.Display(w); err != nil { 2736 toRemove = append(toRemove, i) 2737 } 2738 } 2739 for j := len(toRemove) - 1; j >= 0; j-- { 2740 i := toRemove[j] 2741 cStats = append(cStats[:i], cStats[i+1:]...) 2742 } 2743 if len(cStats) == 0 { 2744 return nil 2745 } 2746 w.Flush() 2747 } 2748 return nil 2749 } 2750 2751 func calcuateCpuPercent(previousCpu, previousSystem uint64, v *stats.Stats) float64 { 2752 var ( 2753 cpuPercent = 0.0 2754 // calculate the change for the cpu usage of the container in between readings 2755 cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCpu) 2756 // calculate the change for the entire system between readings 2757 systemDelta = float64(v.CpuStats.SystemUsage - previousSystem) 2758 ) 2759 2760 if systemDelta > 0.0 && cpuDelta > 0.0 { 2761 cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0 2762 } 2763 return cpuPercent 2764 }