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