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