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