github.com/sbward/docker@v1.4.2-0.20150114010528-c9dab702bed3/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 "github.com/docker/docker/pkg/fileutils" 34 flag "github.com/docker/docker/pkg/mflag" 35 "github.com/docker/docker/pkg/parsers" 36 "github.com/docker/docker/pkg/parsers/filters" 37 "github.com/docker/docker/pkg/promise" 38 "github.com/docker/docker/pkg/signal" 39 "github.com/docker/docker/pkg/term" 40 "github.com/docker/docker/pkg/timeutils" 41 "github.com/docker/docker/pkg/units" 42 "github.com/docker/docker/pkg/urlutil" 43 "github.com/docker/docker/registry" 44 "github.com/docker/docker/runconfig" 45 "github.com/docker/docker/utils" 46 ) 47 48 const ( 49 tarHeaderSize = 512 50 ) 51 52 var ( 53 acceptedImageFilterTags = map[string]struct{}{"dangling": {}} 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 var filename string // path to Dockerfile 152 var origDockerfile string // used for error msg 153 154 if *dockerfileName == "" { 155 // No -f/--file was specified so use the default 156 origDockerfile = api.DefaultDockerfileName 157 *dockerfileName = origDockerfile 158 filename = path.Join(absRoot, *dockerfileName) 159 } else { 160 origDockerfile = *dockerfileName 161 if filename, err = filepath.Abs(*dockerfileName); err != nil { 162 return err 163 } 164 165 // Verify that 'filename' is within the build context 166 if !strings.HasSuffix(absRoot, string(os.PathSeparator)) { 167 absRoot += string(os.PathSeparator) 168 } 169 if !strings.HasPrefix(filename, absRoot) { 170 return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", *dockerfileName, root) 171 } 172 173 // Now reset the dockerfileName to be relative to the build context 174 *dockerfileName = filename[len(absRoot):] 175 } 176 177 if _, err = os.Stat(filename); os.IsNotExist(err) { 178 return fmt.Errorf("Can not locate Dockerfile: %s", origDockerfile) 179 } 180 var includes []string = []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]]' as 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 push := func(authConfig registry.AuthConfig) error { 1219 buf, err := json.Marshal(authConfig) 1220 if err != nil { 1221 return err 1222 } 1223 registryAuthHeader := []string{ 1224 base64.URLEncoding.EncodeToString(buf), 1225 } 1226 1227 return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{ 1228 "X-Registry-Auth": registryAuthHeader, 1229 }) 1230 } 1231 1232 if err := push(authConfig); err != nil { 1233 if strings.Contains(err.Error(), "Status 401") { 1234 fmt.Fprintln(cli.out, "\nPlease login prior to push:") 1235 if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil { 1236 return err 1237 } 1238 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 1239 return push(authConfig) 1240 } 1241 return err 1242 } 1243 return nil 1244 } 1245 1246 func (cli *DockerCli) CmdPull(args ...string) error { 1247 cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry", true) 1248 allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") 1249 cmd.Require(flag.Exact, 1) 1250 1251 utils.ParseFlags(cmd, args, true) 1252 1253 var ( 1254 v = url.Values{} 1255 remote = cmd.Arg(0) 1256 newRemote = remote 1257 ) 1258 taglessRemote, tag := parsers.ParseRepositoryTag(remote) 1259 if tag == "" && !*allTags { 1260 newRemote = taglessRemote + ":" + graph.DEFAULTTAG 1261 } 1262 if tag != "" && *allTags { 1263 return fmt.Errorf("tag can't be used with --all-tags/-a") 1264 } 1265 1266 v.Set("fromImage", newRemote) 1267 1268 // Resolve the Repository name from fqn to RepositoryInfo 1269 repoInfo, err := registry.ParseRepositoryInfo(taglessRemote) 1270 if err != nil { 1271 return err 1272 } 1273 1274 cli.LoadConfigFile() 1275 1276 // Resolve the Auth config relevant for this server 1277 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 1278 1279 pull := func(authConfig registry.AuthConfig) error { 1280 buf, err := json.Marshal(authConfig) 1281 if err != nil { 1282 return err 1283 } 1284 registryAuthHeader := []string{ 1285 base64.URLEncoding.EncodeToString(buf), 1286 } 1287 1288 return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{ 1289 "X-Registry-Auth": registryAuthHeader, 1290 }) 1291 } 1292 1293 if err := pull(authConfig); err != nil { 1294 if strings.Contains(err.Error(), "Status 401") { 1295 fmt.Fprintln(cli.out, "\nPlease login prior to pull:") 1296 if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil { 1297 return err 1298 } 1299 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 1300 return pull(authConfig) 1301 } 1302 return err 1303 } 1304 1305 return nil 1306 } 1307 1308 func (cli *DockerCli) CmdImages(args ...string) error { 1309 cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true) 1310 quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") 1311 all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate image layers)") 1312 noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") 1313 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1314 flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format") 1315 flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format") 1316 1317 flFilter := opts.NewListOpts(nil) 1318 cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')") 1319 cmd.Require(flag.Max, 1) 1320 1321 utils.ParseFlags(cmd, args, true) 1322 1323 // Consolidate all filter flags, and sanity check them early. 1324 // They'll get process in the daemon/server. 1325 imageFilterArgs := filters.Args{} 1326 for _, f := range flFilter.GetAll() { 1327 var err error 1328 imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs) 1329 if err != nil { 1330 return err 1331 } 1332 } 1333 1334 for name := range imageFilterArgs { 1335 if _, ok := acceptedImageFilterTags[name]; !ok { 1336 return fmt.Errorf("Invalid filter '%s'", name) 1337 } 1338 } 1339 1340 matchName := cmd.Arg(0) 1341 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1342 if *flViz || *flTree { 1343 v := url.Values{ 1344 "all": []string{"1"}, 1345 } 1346 if len(imageFilterArgs) > 0 { 1347 filterJson, err := filters.ToParam(imageFilterArgs) 1348 if err != nil { 1349 return err 1350 } 1351 v.Set("filters", filterJson) 1352 } 1353 1354 body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false)) 1355 if err != nil { 1356 return err 1357 } 1358 1359 outs := engine.NewTable("Created", 0) 1360 if _, err := outs.ReadListFrom(body); err != nil { 1361 return err 1362 } 1363 1364 var ( 1365 printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string) 1366 startImage *engine.Env 1367 1368 roots = engine.NewTable("Created", outs.Len()) 1369 byParent = make(map[string]*engine.Table) 1370 ) 1371 1372 for _, image := range outs.Data { 1373 if image.Get("ParentId") == "" { 1374 roots.Add(image) 1375 } else { 1376 if children, exists := byParent[image.Get("ParentId")]; exists { 1377 children.Add(image) 1378 } else { 1379 byParent[image.Get("ParentId")] = engine.NewTable("Created", 1) 1380 byParent[image.Get("ParentId")].Add(image) 1381 } 1382 } 1383 1384 if matchName != "" { 1385 if matchName == image.Get("Id") || matchName == utils.TruncateID(image.Get("Id")) { 1386 startImage = image 1387 } 1388 1389 for _, repotag := range image.GetList("RepoTags") { 1390 if repotag == matchName { 1391 startImage = image 1392 } 1393 } 1394 } 1395 } 1396 1397 if *flViz { 1398 fmt.Fprintf(cli.out, "digraph docker {\n") 1399 printNode = (*DockerCli).printVizNode 1400 } else { 1401 printNode = (*DockerCli).printTreeNode 1402 } 1403 1404 if startImage != nil { 1405 root := engine.NewTable("Created", 1) 1406 root.Add(startImage) 1407 cli.WalkTree(*noTrunc, root, byParent, "", printNode) 1408 } else if matchName == "" { 1409 cli.WalkTree(*noTrunc, roots, byParent, "", printNode) 1410 } 1411 if *flViz { 1412 fmt.Fprintf(cli.out, " base [style=invisible]\n}\n") 1413 } 1414 } else { 1415 v := url.Values{} 1416 if len(imageFilterArgs) > 0 { 1417 filterJson, err := filters.ToParam(imageFilterArgs) 1418 if err != nil { 1419 return err 1420 } 1421 v.Set("filters", filterJson) 1422 } 1423 1424 if cmd.NArg() == 1 { 1425 // FIXME rename this parameter, to not be confused with the filters flag 1426 v.Set("filter", matchName) 1427 } 1428 if *all { 1429 v.Set("all", "1") 1430 } 1431 1432 body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false)) 1433 1434 if err != nil { 1435 return err 1436 } 1437 1438 outs := engine.NewTable("Created", 0) 1439 if _, err := outs.ReadListFrom(body); err != nil { 1440 return err 1441 } 1442 1443 w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 1444 if !*quiet { 1445 fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE") 1446 } 1447 1448 for _, out := range outs.Data { 1449 for _, repotag := range out.GetList("RepoTags") { 1450 1451 repo, tag := parsers.ParseRepositoryTag(repotag) 1452 outID := out.Get("Id") 1453 if !*noTrunc { 1454 outID = utils.TruncateID(outID) 1455 } 1456 1457 if !*quiet { 1458 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")))) 1459 } else { 1460 fmt.Fprintln(w, outID) 1461 } 1462 } 1463 } 1464 1465 if !*quiet { 1466 w.Flush() 1467 } 1468 } 1469 return nil 1470 } 1471 1472 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1473 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)) { 1474 length := images.Len() 1475 if length > 1 { 1476 for index, image := range images.Data { 1477 if index+1 == length { 1478 printNode(cli, noTrunc, image, prefix+"└─") 1479 if subimages, exists := byParent[image.Get("Id")]; exists { 1480 cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode) 1481 } 1482 } else { 1483 printNode(cli, noTrunc, image, prefix+"\u251C─") 1484 if subimages, exists := byParent[image.Get("Id")]; exists { 1485 cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode) 1486 } 1487 } 1488 } 1489 } else { 1490 for _, image := range images.Data { 1491 printNode(cli, noTrunc, image, prefix+"└─") 1492 if subimages, exists := byParent[image.Get("Id")]; exists { 1493 cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode) 1494 } 1495 } 1496 } 1497 } 1498 1499 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1500 func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) { 1501 var ( 1502 imageID string 1503 parentID string 1504 ) 1505 if noTrunc { 1506 imageID = image.Get("Id") 1507 parentID = image.Get("ParentId") 1508 } else { 1509 imageID = utils.TruncateID(image.Get("Id")) 1510 parentID = utils.TruncateID(image.Get("ParentId")) 1511 } 1512 if parentID == "" { 1513 fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID) 1514 } else { 1515 fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID) 1516 } 1517 if image.GetList("RepoTags")[0] != "<none>:<none>" { 1518 fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", 1519 imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n")) 1520 } 1521 } 1522 1523 // FIXME: --viz and --tree are deprecated. Remove them in a future version. 1524 func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) { 1525 var imageID string 1526 if noTrunc { 1527 imageID = image.Get("Id") 1528 } else { 1529 imageID = utils.TruncateID(image.Get("Id")) 1530 } 1531 1532 fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.GetInt64("VirtualSize")))) 1533 if image.GetList("RepoTags")[0] != "<none>:<none>" { 1534 fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", ")) 1535 } else { 1536 fmt.Fprint(cli.out, "\n") 1537 } 1538 } 1539 1540 func (cli *DockerCli) CmdPs(args ...string) error { 1541 var ( 1542 err error 1543 1544 psFilterArgs = filters.Args{} 1545 v = url.Values{} 1546 1547 cmd = cli.Subcmd("ps", "", "List containers", true) 1548 quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") 1549 size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes") 1550 all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") 1551 noTrunc = cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") 1552 nLatest = cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.") 1553 since = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.") 1554 before = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.") 1555 last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.") 1556 flFilter = opts.NewListOpts(nil) 1557 ) 1558 cmd.Require(flag.Exact, 0) 1559 1560 cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>\nstatus=(restarting|running|paused|exited)") 1561 1562 utils.ParseFlags(cmd, args, true) 1563 if *last == -1 && *nLatest { 1564 *last = 1 1565 } 1566 1567 if *all { 1568 v.Set("all", "1") 1569 } 1570 1571 if *last != -1 { 1572 v.Set("limit", strconv.Itoa(*last)) 1573 } 1574 1575 if *since != "" { 1576 v.Set("since", *since) 1577 } 1578 1579 if *before != "" { 1580 v.Set("before", *before) 1581 } 1582 1583 if *size { 1584 v.Set("size", "1") 1585 } 1586 1587 // Consolidate all filter flags, and sanity check them. 1588 // They'll get processed in the daemon/server. 1589 for _, f := range flFilter.GetAll() { 1590 if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil { 1591 return err 1592 } 1593 } 1594 1595 if len(psFilterArgs) > 0 { 1596 filterJson, err := filters.ToParam(psFilterArgs) 1597 if err != nil { 1598 return err 1599 } 1600 1601 v.Set("filters", filterJson) 1602 } 1603 1604 body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false)) 1605 if err != nil { 1606 return err 1607 } 1608 1609 outs := engine.NewTable("Created", 0) 1610 if _, err := outs.ReadListFrom(body); err != nil { 1611 return err 1612 } 1613 1614 w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 1615 if !*quiet { 1616 fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") 1617 1618 if *size { 1619 fmt.Fprintln(w, "\tSIZE") 1620 } else { 1621 fmt.Fprint(w, "\n") 1622 } 1623 } 1624 1625 stripNamePrefix := func(ss []string) []string { 1626 for i, s := range ss { 1627 ss[i] = s[1:] 1628 } 1629 1630 return ss 1631 } 1632 1633 for _, out := range outs.Data { 1634 outID := out.Get("Id") 1635 1636 if !*noTrunc { 1637 outID = utils.TruncateID(outID) 1638 } 1639 1640 if *quiet { 1641 fmt.Fprintln(w, outID) 1642 1643 continue 1644 } 1645 1646 var ( 1647 outNames = stripNamePrefix(out.GetList("Names")) 1648 outCommand = strconv.Quote(out.Get("Command")) 1649 ports = engine.NewTable("", 0) 1650 ) 1651 1652 if !*noTrunc { 1653 outCommand = utils.Trunc(outCommand, 20) 1654 1655 // only display the default name for the container with notrunc is passed 1656 for _, name := range outNames { 1657 if len(strings.Split(name, "/")) == 1 { 1658 outNames = []string{name} 1659 1660 break 1661 } 1662 } 1663 } 1664 1665 ports.ReadListFrom([]byte(out.Get("Ports"))) 1666 1667 image := out.Get("Image") 1668 if image == "" { 1669 image = "<no image>" 1670 } 1671 1672 fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand, 1673 units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), 1674 out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ",")) 1675 1676 if *size { 1677 if out.GetInt("SizeRootFs") > 0 { 1678 fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(out.GetInt64("SizeRw"))), units.HumanSize(float64(out.GetInt64("SizeRootFs")))) 1679 } else { 1680 fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("SizeRw")))) 1681 } 1682 1683 continue 1684 } 1685 1686 fmt.Fprint(w, "\n") 1687 } 1688 1689 if !*quiet { 1690 w.Flush() 1691 } 1692 1693 return nil 1694 } 1695 1696 func (cli *DockerCli) CmdCommit(args ...string) error { 1697 cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes", true) 1698 flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit") 1699 flComment := cmd.String([]string{"m", "-message"}, "", "Commit message") 1700 flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")") 1701 // FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands. 1702 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") 1703 cmd.Require(flag.Max, 2) 1704 cmd.Require(flag.Min, 1) 1705 utils.ParseFlags(cmd, args, true) 1706 1707 var ( 1708 name = cmd.Arg(0) 1709 repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) 1710 ) 1711 1712 //Check if the given image name can be resolved 1713 if repository != "" { 1714 if err := registry.ValidateRepositoryName(repository); err != nil { 1715 return err 1716 } 1717 } 1718 1719 v := url.Values{} 1720 v.Set("container", name) 1721 v.Set("repo", repository) 1722 v.Set("tag", tag) 1723 v.Set("comment", *flComment) 1724 v.Set("author", *flAuthor) 1725 1726 if *flPause != true { 1727 v.Set("pause", "0") 1728 } 1729 1730 var ( 1731 config *runconfig.Config 1732 env engine.Env 1733 ) 1734 if *flConfig != "" { 1735 config = &runconfig.Config{} 1736 if err := json.Unmarshal([]byte(*flConfig), config); err != nil { 1737 return err 1738 } 1739 } 1740 stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false) 1741 if err != nil { 1742 return err 1743 } 1744 if err := env.Decode(stream); err != nil { 1745 return err 1746 } 1747 1748 fmt.Fprintf(cli.out, "%s\n", env.Get("Id")) 1749 return nil 1750 } 1751 1752 func (cli *DockerCli) CmdEvents(args ...string) error { 1753 cmd := cli.Subcmd("events", "", "Get real time events from the server", true) 1754 since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp") 1755 until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") 1756 flFilter := opts.NewListOpts(nil) 1757 cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'event=stop')") 1758 cmd.Require(flag.Exact, 0) 1759 1760 utils.ParseFlags(cmd, args, true) 1761 1762 var ( 1763 v = url.Values{} 1764 loc = time.FixedZone(time.Now().Zone()) 1765 eventFilterArgs = filters.Args{} 1766 ) 1767 1768 // Consolidate all filter flags, and sanity check them early. 1769 // They'll get process in the daemon/server. 1770 for _, f := range flFilter.GetAll() { 1771 var err error 1772 eventFilterArgs, err = filters.ParseFlag(f, eventFilterArgs) 1773 if err != nil { 1774 return err 1775 } 1776 } 1777 var setTime = func(key, value string) { 1778 format := timeutils.RFC3339NanoFixed 1779 if len(value) < len(format) { 1780 format = format[:len(value)] 1781 } 1782 if t, err := time.ParseInLocation(format, value, loc); err == nil { 1783 v.Set(key, strconv.FormatInt(t.Unix(), 10)) 1784 } else { 1785 v.Set(key, value) 1786 } 1787 } 1788 if *since != "" { 1789 setTime("since", *since) 1790 } 1791 if *until != "" { 1792 setTime("until", *until) 1793 } 1794 if len(eventFilterArgs) > 0 { 1795 filterJson, err := filters.ToParam(eventFilterArgs) 1796 if err != nil { 1797 return err 1798 } 1799 v.Set("filters", filterJson) 1800 } 1801 if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil { 1802 return err 1803 } 1804 return nil 1805 } 1806 1807 func (cli *DockerCli) CmdExport(args ...string) error { 1808 cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT", true) 1809 cmd.Require(flag.Exact, 1) 1810 1811 utils.ParseFlags(cmd, args, true) 1812 1813 if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil { 1814 return err 1815 } 1816 return nil 1817 } 1818 1819 func (cli *DockerCli) CmdDiff(args ...string) error { 1820 cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem", true) 1821 cmd.Require(flag.Exact, 1) 1822 1823 utils.ParseFlags(cmd, args, true) 1824 1825 body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false)) 1826 1827 if err != nil { 1828 return err 1829 } 1830 1831 outs := engine.NewTable("", 0) 1832 if _, err := outs.ReadListFrom(body); err != nil { 1833 return err 1834 } 1835 for _, change := range outs.Data { 1836 var kind string 1837 switch change.GetInt("Kind") { 1838 case archive.ChangeModify: 1839 kind = "C" 1840 case archive.ChangeAdd: 1841 kind = "A" 1842 case archive.ChangeDelete: 1843 kind = "D" 1844 } 1845 fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path")) 1846 } 1847 return nil 1848 } 1849 1850 func (cli *DockerCli) CmdLogs(args ...string) error { 1851 var ( 1852 cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container", true) 1853 follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output") 1854 times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps") 1855 tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)") 1856 ) 1857 cmd.Require(flag.Exact, 1) 1858 1859 utils.ParseFlags(cmd, args, true) 1860 1861 name := cmd.Arg(0) 1862 1863 stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) 1864 if err != nil { 1865 return err 1866 } 1867 1868 env := engine.Env{} 1869 if err := env.Decode(stream); err != nil { 1870 return err 1871 } 1872 1873 v := url.Values{} 1874 v.Set("stdout", "1") 1875 v.Set("stderr", "1") 1876 1877 if *times { 1878 v.Set("timestamps", "1") 1879 } 1880 1881 if *follow { 1882 v.Set("follow", "1") 1883 } 1884 v.Set("tail", *tail) 1885 1886 return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil) 1887 } 1888 1889 func (cli *DockerCli) CmdAttach(args ...string) error { 1890 var ( 1891 cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container", true) 1892 noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN") 1893 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.") 1894 ) 1895 cmd.Require(flag.Exact, 1) 1896 1897 utils.ParseFlags(cmd, args, true) 1898 name := cmd.Arg(0) 1899 1900 stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) 1901 if err != nil { 1902 return err 1903 } 1904 1905 env := engine.Env{} 1906 if err := env.Decode(stream); err != nil { 1907 return err 1908 } 1909 1910 if !env.GetSubEnv("State").GetBool("Running") { 1911 return fmt.Errorf("You cannot attach to a stopped container, start it first") 1912 } 1913 1914 var ( 1915 config = env.GetSubEnv("Config") 1916 tty = config.GetBool("Tty") 1917 ) 1918 1919 if err := cli.CheckTtyInput(!*noStdin, tty); err != nil { 1920 return err 1921 } 1922 1923 if tty && cli.isTerminalOut { 1924 if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { 1925 log.Debugf("Error monitoring TTY size: %s", err) 1926 } 1927 } 1928 1929 var in io.ReadCloser 1930 1931 v := url.Values{} 1932 v.Set("stream", "1") 1933 if !*noStdin && config.GetBool("OpenStdin") { 1934 v.Set("stdin", "1") 1935 in = cli.in 1936 } 1937 1938 v.Set("stdout", "1") 1939 v.Set("stderr", "1") 1940 1941 if *proxy && !tty { 1942 sigc := cli.forwardAllSignals(cmd.Arg(0)) 1943 defer signal.StopCatch(sigc) 1944 } 1945 1946 if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil); err != nil { 1947 return err 1948 } 1949 1950 _, status, err := getExitCode(cli, cmd.Arg(0)) 1951 if err != nil { 1952 return err 1953 } 1954 if status != 0 { 1955 return &utils.StatusError{StatusCode: status} 1956 } 1957 1958 return nil 1959 } 1960 1961 func (cli *DockerCli) CmdSearch(args ...string) error { 1962 cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images", true) 1963 noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") 1964 trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds") 1965 automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds") 1966 stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars") 1967 cmd.Require(flag.Exact, 1) 1968 1969 utils.ParseFlags(cmd, args, true) 1970 1971 v := url.Values{} 1972 v.Set("term", cmd.Arg(0)) 1973 1974 body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true)) 1975 1976 if err != nil { 1977 return err 1978 } 1979 outs := engine.NewTable("star_count", 0) 1980 if _, err := outs.ReadListFrom(body); err != nil { 1981 return err 1982 } 1983 w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0) 1984 fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n") 1985 for _, out := range outs.Data { 1986 if ((*automated || *trusted) && (!out.GetBool("is_trusted") && !out.GetBool("is_automated"))) || (*stars > out.GetInt("star_count")) { 1987 continue 1988 } 1989 desc := strings.Replace(out.Get("description"), "\n", " ", -1) 1990 desc = strings.Replace(desc, "\r", " ", -1) 1991 if !*noTrunc && len(desc) > 45 { 1992 desc = utils.Trunc(desc, 42) + "..." 1993 } 1994 fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count")) 1995 if out.GetBool("is_official") { 1996 fmt.Fprint(w, "[OK]") 1997 1998 } 1999 fmt.Fprint(w, "\t") 2000 if out.GetBool("is_automated") || out.GetBool("is_trusted") { 2001 fmt.Fprint(w, "[OK]") 2002 } 2003 fmt.Fprint(w, "\n") 2004 } 2005 w.Flush() 2006 return nil 2007 } 2008 2009 // Ports type - Used to parse multiple -p flags 2010 type ports []int 2011 2012 func (cli *DockerCli) CmdTag(args ...string) error { 2013 cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository", true) 2014 force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") 2015 cmd.Require(flag.Exact, 2) 2016 2017 utils.ParseFlags(cmd, args, true) 2018 2019 var ( 2020 repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) 2021 v = url.Values{} 2022 ) 2023 2024 //Check if the given image name can be resolved 2025 if err := registry.ValidateRepositoryName(repository); err != nil { 2026 return err 2027 } 2028 v.Set("repo", repository) 2029 v.Set("tag", tag) 2030 2031 if *force { 2032 v.Set("force", "1") 2033 } 2034 2035 if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil { 2036 return err 2037 } 2038 return nil 2039 } 2040 2041 func (cli *DockerCli) pullImage(image string) error { 2042 return cli.pullImageCustomOut(image, cli.out) 2043 } 2044 2045 func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { 2046 v := url.Values{} 2047 repos, tag := parsers.ParseRepositoryTag(image) 2048 // pull only the image tagged 'latest' if no tag was specified 2049 if tag == "" { 2050 tag = graph.DEFAULTTAG 2051 } 2052 v.Set("fromImage", repos) 2053 v.Set("tag", tag) 2054 2055 // Resolve the Repository name from fqn to RepositoryInfo 2056 repoInfo, err := registry.ParseRepositoryInfo(repos) 2057 if err != nil { 2058 return err 2059 } 2060 2061 // Load the auth config file, to be able to pull the image 2062 cli.LoadConfigFile() 2063 2064 // Resolve the Auth config relevant for this server 2065 authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) 2066 buf, err := json.Marshal(authConfig) 2067 if err != nil { 2068 return err 2069 } 2070 2071 registryAuthHeader := []string{ 2072 base64.URLEncoding.EncodeToString(buf), 2073 } 2074 if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { 2075 return err 2076 } 2077 return nil 2078 } 2079 2080 type cidFile struct { 2081 path string 2082 file *os.File 2083 written bool 2084 } 2085 2086 func newCIDFile(path string) (*cidFile, error) { 2087 if _, err := os.Stat(path); err == nil { 2088 return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path) 2089 } 2090 2091 f, err := os.Create(path) 2092 if err != nil { 2093 return nil, fmt.Errorf("Failed to create the container ID file: %s", err) 2094 } 2095 2096 return &cidFile{path: path, file: f}, nil 2097 } 2098 2099 func (cid *cidFile) Close() error { 2100 cid.file.Close() 2101 2102 if !cid.written { 2103 if err := os.Remove(cid.path); err != nil { 2104 return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err) 2105 } 2106 } 2107 2108 return nil 2109 } 2110 2111 func (cid *cidFile) Write(id string) error { 2112 if _, err := cid.file.Write([]byte(id)); err != nil { 2113 return fmt.Errorf("Failed to write the container ID to the file: %s", err) 2114 } 2115 cid.written = true 2116 return nil 2117 } 2118 2119 func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) { 2120 containerValues := url.Values{} 2121 if name != "" { 2122 containerValues.Set("name", name) 2123 } 2124 2125 mergedConfig := runconfig.MergeConfigs(config, hostConfig) 2126 2127 var containerIDFile *cidFile 2128 if cidfile != "" { 2129 var err error 2130 if containerIDFile, err = newCIDFile(cidfile); err != nil { 2131 return nil, err 2132 } 2133 defer containerIDFile.Close() 2134 } 2135 2136 //create the container 2137 stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false) 2138 //if image not found try to pull it 2139 if statusCode == 404 { 2140 repo, tag := parsers.ParseRepositoryTag(config.Image) 2141 if tag == "" { 2142 tag = graph.DEFAULTTAG 2143 } 2144 fmt.Fprintf(cli.err, "Unable to find image '%s:%s' locally\n", repo, tag) 2145 2146 // we don't want to write to stdout anything apart from container.ID 2147 if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { 2148 return nil, err 2149 } 2150 // Retry 2151 if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil { 2152 return nil, err 2153 } 2154 } else if err != nil { 2155 return nil, err 2156 } 2157 2158 var result engine.Env 2159 if err := result.Decode(stream); err != nil { 2160 return nil, err 2161 } 2162 2163 for _, warning := range result.GetList("Warnings") { 2164 fmt.Fprintf(cli.err, "WARNING: %s\n", warning) 2165 } 2166 2167 if containerIDFile != nil { 2168 if err = containerIDFile.Write(result.Get("Id")); err != nil { 2169 return nil, err 2170 } 2171 } 2172 2173 return result, nil 2174 2175 } 2176 2177 func (cli *DockerCli) CmdCreate(args ...string) error { 2178 cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true) 2179 2180 // These are flags not stored in Config/HostConfig 2181 var ( 2182 flName = cmd.String([]string{"-name"}, "", "Assign a name to the container") 2183 ) 2184 2185 config, hostConfig, cmd, err := runconfig.Parse(cmd, args) 2186 if err != nil { 2187 utils.ReportError(cmd, err.Error(), true) 2188 } 2189 if config.Image == "" { 2190 cmd.Usage() 2191 return nil 2192 } 2193 2194 createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) 2195 if err != nil { 2196 return err 2197 } 2198 2199 fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id")) 2200 2201 return nil 2202 } 2203 2204 func (cli *DockerCli) CmdRun(args ...string) error { 2205 // FIXME: just use runconfig.Parse already 2206 cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container", true) 2207 2208 // These are flags not stored in Config/HostConfig 2209 var ( 2210 flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") 2211 flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run the container in the background and print the new container ID") 2212 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.") 2213 flName = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container") 2214 flAttach *opts.ListOpts 2215 2216 ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d") 2217 ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm") 2218 ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d") 2219 ) 2220 2221 config, hostConfig, cmd, err := runconfig.Parse(cmd, args) 2222 // just in case the Parse does not exit 2223 if err != nil { 2224 utils.ReportError(cmd, err.Error(), true) 2225 } 2226 if config.Image == "" { 2227 cmd.Usage() 2228 return nil 2229 } 2230 2231 if !*flDetach { 2232 if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil { 2233 return err 2234 } 2235 } else { 2236 if fl := cmd.Lookup("attach"); fl != nil { 2237 flAttach = fl.Value.(*opts.ListOpts) 2238 if flAttach.Len() != 0 { 2239 return ErrConflictAttachDetach 2240 } 2241 } 2242 if *flAutoRemove { 2243 return ErrConflictDetachAutoRemove 2244 } 2245 2246 config.AttachStdin = false 2247 config.AttachStdout = false 2248 config.AttachStderr = false 2249 config.StdinOnce = false 2250 } 2251 2252 // Disable flSigProxy when in TTY mode 2253 sigProxy := *flSigProxy 2254 if config.Tty { 2255 sigProxy = false 2256 } 2257 2258 runResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) 2259 if err != nil { 2260 return err 2261 } 2262 2263 if sigProxy { 2264 sigc := cli.forwardAllSignals(runResult.Get("Id")) 2265 defer signal.StopCatch(sigc) 2266 } 2267 2268 var ( 2269 waitDisplayId chan struct{} 2270 errCh chan error 2271 ) 2272 2273 if !config.AttachStdout && !config.AttachStderr { 2274 // Make this asynchronous to allow the client to write to stdin before having to read the ID 2275 waitDisplayId = make(chan struct{}) 2276 go func() { 2277 defer close(waitDisplayId) 2278 fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id")) 2279 }() 2280 } 2281 2282 if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") { 2283 return ErrConflictRestartPolicyAndAutoRemove 2284 } 2285 2286 // We need to instantiate the chan because the select needs it. It can 2287 // be closed but can't be uninitialized. 2288 hijacked := make(chan io.Closer) 2289 2290 // Block the return until the chan gets closed 2291 defer func() { 2292 log.Debugf("End of CmdRun(), Waiting for hijack to finish.") 2293 if _, ok := <-hijacked; ok { 2294 log.Errorf("Hijack did not finish (chan still open)") 2295 } 2296 }() 2297 2298 if config.AttachStdin || config.AttachStdout || config.AttachStderr { 2299 var ( 2300 out, stderr io.Writer 2301 in io.ReadCloser 2302 v = url.Values{} 2303 ) 2304 v.Set("stream", "1") 2305 2306 if config.AttachStdin { 2307 v.Set("stdin", "1") 2308 in = cli.in 2309 } 2310 if config.AttachStdout { 2311 v.Set("stdout", "1") 2312 out = cli.out 2313 } 2314 if config.AttachStderr { 2315 v.Set("stderr", "1") 2316 if config.Tty { 2317 stderr = cli.out 2318 } else { 2319 stderr = cli.err 2320 } 2321 } 2322 2323 errCh = promise.Go(func() error { 2324 return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil) 2325 }) 2326 } else { 2327 close(hijacked) 2328 } 2329 2330 // Acknowledge the hijack before starting 2331 select { 2332 case closer := <-hijacked: 2333 // Make sure that the hijack gets closed when returning (results 2334 // in closing the hijack chan and freeing server's goroutines) 2335 if closer != nil { 2336 defer closer.Close() 2337 } 2338 case err := <-errCh: 2339 if err != nil { 2340 log.Debugf("Error hijack: %s", err) 2341 return err 2342 } 2343 } 2344 2345 //start the container 2346 if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", nil, false)); err != nil { 2347 return err 2348 } 2349 2350 if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { 2351 if err := cli.monitorTtySize(runResult.Get("Id"), false); err != nil { 2352 log.Errorf("Error monitoring TTY size: %s", err) 2353 } 2354 } 2355 2356 if errCh != nil { 2357 if err := <-errCh; err != nil { 2358 log.Debugf("Error hijack: %s", err) 2359 return err 2360 } 2361 } 2362 2363 // Detached mode: wait for the id to be displayed and return. 2364 if !config.AttachStdout && !config.AttachStderr { 2365 // Detached mode 2366 <-waitDisplayId 2367 return nil 2368 } 2369 2370 var status int 2371 2372 // Attached mode 2373 if *flAutoRemove { 2374 // Autoremove: wait for the container to finish, retrieve 2375 // the exit code and remove the container 2376 if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil { 2377 return err 2378 } 2379 if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { 2380 return err 2381 } 2382 if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil { 2383 return err 2384 } 2385 } else { 2386 // No Autoremove: Simply retrieve the exit code 2387 if !config.Tty { 2388 // In non-TTY mode, we can't detach, so we must wait for container exit 2389 if status, err = waitForExit(cli, runResult.Get("Id")); err != nil { 2390 return err 2391 } 2392 } else { 2393 // In TTY mode, there is a race: if the process dies too slowly, the state could 2394 // be updated after the getExitCode call and result in the wrong exit code being reported 2395 if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil { 2396 return err 2397 } 2398 } 2399 } 2400 if status != 0 { 2401 return &utils.StatusError{StatusCode: status} 2402 } 2403 return nil 2404 } 2405 2406 func (cli *DockerCli) CmdCp(args ...string) error { 2407 cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH", true) 2408 cmd.Require(flag.Exact, 2) 2409 2410 utils.ParseFlags(cmd, args, true) 2411 2412 var copyData engine.Env 2413 info := strings.Split(cmd.Arg(0), ":") 2414 2415 if len(info) != 2 { 2416 return fmt.Errorf("Error: Path not specified") 2417 } 2418 2419 copyData.Set("Resource", info[1]) 2420 copyData.Set("HostPath", cmd.Arg(1)) 2421 2422 stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false) 2423 if stream != nil { 2424 defer stream.Close() 2425 } 2426 if statusCode == 404 { 2427 return fmt.Errorf("No such container: %v", info[0]) 2428 } 2429 if err != nil { 2430 return err 2431 } 2432 2433 if statusCode == 200 { 2434 if err := archive.Untar(stream, copyData.Get("HostPath"), &archive.TarOptions{NoLchown: true}); err != nil { 2435 return err 2436 } 2437 } 2438 return nil 2439 } 2440 2441 func (cli *DockerCli) CmdSave(args ...string) error { 2442 cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)", true) 2443 outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT") 2444 cmd.Require(flag.Min, 1) 2445 2446 utils.ParseFlags(cmd, args, true) 2447 2448 var ( 2449 output io.Writer = cli.out 2450 err error 2451 ) 2452 if *outfile != "" { 2453 output, err = os.Create(*outfile) 2454 if err != nil { 2455 return err 2456 } 2457 } else if cli.isTerminalOut { 2458 return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.") 2459 } 2460 2461 if len(cmd.Args()) == 1 { 2462 image := cmd.Arg(0) 2463 if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil { 2464 return err 2465 } 2466 } else { 2467 v := url.Values{} 2468 for _, arg := range cmd.Args() { 2469 v.Add("names", arg) 2470 } 2471 if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil { 2472 return err 2473 } 2474 } 2475 return nil 2476 } 2477 2478 func (cli *DockerCli) CmdLoad(args ...string) error { 2479 cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN", true) 2480 infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN") 2481 cmd.Require(flag.Exact, 0) 2482 2483 utils.ParseFlags(cmd, args, true) 2484 2485 var ( 2486 input io.Reader = cli.in 2487 err error 2488 ) 2489 if *infile != "" { 2490 input, err = os.Open(*infile) 2491 if err != nil { 2492 return err 2493 } 2494 } 2495 if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil { 2496 return err 2497 } 2498 return nil 2499 } 2500 2501 func (cli *DockerCli) CmdExec(args ...string) error { 2502 cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true) 2503 2504 execConfig, err := runconfig.ParseExec(cmd, args) 2505 // just in case the ParseExec does not exit 2506 if execConfig.Container == "" || err != nil { 2507 return &utils.StatusError{StatusCode: 1} 2508 } 2509 2510 stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false) 2511 if err != nil { 2512 return err 2513 } 2514 2515 var execResult engine.Env 2516 if err := execResult.Decode(stream); err != nil { 2517 return err 2518 } 2519 2520 execID := execResult.Get("Id") 2521 2522 if execID == "" { 2523 fmt.Fprintf(cli.out, "exec ID empty") 2524 return nil 2525 } 2526 2527 if !execConfig.Detach { 2528 if err := cli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil { 2529 return err 2530 } 2531 } else { 2532 if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil { 2533 return err 2534 } 2535 // For now don't print this - wait for when we support exec wait() 2536 // fmt.Fprintf(cli.out, "%s\n", execID) 2537 return nil 2538 } 2539 2540 // Interactive exec requested. 2541 var ( 2542 out, stderr io.Writer 2543 in io.ReadCloser 2544 hijacked = make(chan io.Closer) 2545 errCh chan error 2546 ) 2547 2548 // Block the return until the chan gets closed 2549 defer func() { 2550 log.Debugf("End of CmdExec(), Waiting for hijack to finish.") 2551 if _, ok := <-hijacked; ok { 2552 log.Errorf("Hijack did not finish (chan still open)") 2553 } 2554 }() 2555 2556 if execConfig.AttachStdin { 2557 in = cli.in 2558 } 2559 if execConfig.AttachStdout { 2560 out = cli.out 2561 } 2562 if execConfig.AttachStderr { 2563 if execConfig.Tty { 2564 stderr = cli.out 2565 } else { 2566 stderr = cli.err 2567 } 2568 } 2569 errCh = promise.Go(func() error { 2570 return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig) 2571 }) 2572 2573 // Acknowledge the hijack before starting 2574 select { 2575 case closer := <-hijacked: 2576 // Make sure that hijack gets closed when returning. (result 2577 // in closing hijack chan and freeing server's goroutines. 2578 if closer != nil { 2579 defer closer.Close() 2580 } 2581 case err := <-errCh: 2582 if err != nil { 2583 log.Debugf("Error hijack: %s", err) 2584 return err 2585 } 2586 } 2587 2588 if execConfig.Tty && cli.isTerminalIn { 2589 if err := cli.monitorTtySize(execID, true); err != nil { 2590 log.Errorf("Error monitoring TTY size: %s", err) 2591 } 2592 } 2593 2594 if err := <-errCh; err != nil { 2595 log.Debugf("Error hijack: %s", err) 2596 return err 2597 } 2598 2599 var status int 2600 if _, status, err = getExecExitCode(cli, execID); err != nil { 2601 return err 2602 } 2603 2604 if status != 0 { 2605 return &utils.StatusError{StatusCode: status} 2606 } 2607 2608 return nil 2609 }