1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/api/client/commands.go

2248 lines
62 KiB
Go
Raw Normal View History

package client
import (
"bufio"
2013-02-13 17:28:13 -08:00
"bytes"
"encoding/base64"
2013-02-13 17:28:13 -08:00
"encoding/json"
"fmt"
2013-02-13 17:28:13 -08:00
"io"
2013-04-22 18:17:47 +02:00
"io/ioutil"
"net/http"
2013-03-13 18:37:00 -07:00
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
2013-02-28 11:52:22 -08:00
"strconv"
2013-02-13 17:28:13 -08:00
"strings"
2013-05-24 11:07:32 -07:00
"syscall"
2013-02-13 17:28:13 -08:00
"text/tabwriter"
"text/template"
2013-02-13 17:28:13 -08:00
"time"
"github.com/dotcloud/docker/api"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/opts"
"github.com/dotcloud/docker/pkg/signal"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/pkg/units"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"github.com/dotcloud/docker/utils/filters"
2013-09-20 11:31:00 -07:00
)
const (
tarHeaderSize = 512
)
func (cli *DockerCli) CmdHelp(args ...string) error {
if len(args) > 0 {
method, exists := cli.getMethod(args[0])
if !exists {
fmt.Fprintf(cli.err, "Error: Command not found: %s\n", args[0])
} else {
method("--help")
return nil
}
}
help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", api.DEFAULTUNIXSOCKET)
for _, command := range [][]string{
2013-05-30 14:08:26 +00:00
{"attach", "Attach to a running container"},
{"build", "Build an image from a Dockerfile"},
2013-05-30 14:08:26 +00:00
{"commit", "Create a new image from a container's changes"},
{"cp", "Copy files/folders from a container's filesystem to the host path"},
2013-05-30 14:08:26 +00:00
{"diff", "Inspect changes on a container's filesystem"},
2013-07-10 12:55:05 +00:00
{"events", "Get real time events from the server"},
2013-05-30 14:08:26 +00:00
{"export", "Stream the contents of a container as a tar archive"},
{"history", "Show the history of an image"},
{"images", "List images"},
{"import", "Create a new filesystem image from the contents of a tarball"},
{"info", "Display system-wide information"},
{"inspect", "Return low-level information on a container"},
{"kill", "Kill a running container"},
2013-09-02 09:06:17 -07:00
{"load", "Load an image from a tar archive"},
{"login", "Register or log in to the Docker registry server"},
2013-05-30 14:08:26 +00:00
{"logs", "Fetch the logs of a container"},
{"port", "Lookup the public-facing port that is NAT-ed to PRIVATE_PORT"},
{"pause", "Pause all processes within a container"},
2013-05-30 14:08:26 +00:00
{"ps", "List containers"},
{"pull", "Pull an image or a repository from a Docker registry server"},
{"push", "Push an image or a repository to a Docker registry server"},
2013-05-30 14:08:26 +00:00
{"restart", "Restart a running container"},
2013-07-17 13:46:11 -04:00
{"rm", "Remove one or more containers"},
{"rmi", "Remove one or more images"},
2013-05-30 14:08:26 +00:00
{"run", "Run a command in a new container"},
2013-09-02 09:06:17 -07:00
{"save", "Save an image to a tar archive"},
{"search", "Search for an image on the Docker Hub"},
2013-05-30 14:08:26 +00:00
{"start", "Start a stopped container"},
{"stop", "Stop a running container"},
{"tag", "Tag an image into a repository"},
{"top", "Lookup the running processes of a container"},
{"unpause", "Unpause a paused container"},
{"version", "Show the Docker version information"},
{"wait", "Block until a container stops, then print its exit code"},
} {
2013-05-30 14:08:26 +00:00
help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1])
}
fmt.Fprintf(cli.err, "%s\n", help)
2013-04-22 18:17:47 +02:00
return nil
}
func (cli *DockerCli) CmdBuild(args ...string) error {
cmd := cli.Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new image from the source code at PATH")
tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success")
suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds")
2013-04-24 11:03:01 -07:00
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
2013-05-19 10:46:24 -07:00
var (
2013-10-31 16:57:45 -07:00
context archive.Archive
isRemote bool
err error
2013-05-19 10:46:24 -07:00
)
2013-05-07 19:23:50 +02:00
_, err = exec.LookPath("git")
hasGit := err == nil
if cmd.Arg(0) == "-" {
// As a special case, 'docker build -' will build from either an empty context with the
// contents of stdin as a Dockerfile, or a tar-ed context from stdin.
buf := bufio.NewReader(cli.in)
magic, err := buf.Peek(tarHeaderSize)
if err != nil && err != io.EOF {
return fmt.Errorf("failed to peek context header from STDIN: %v", err)
}
if !archive.IsArchive(magic) {
dockerfile, err := ioutil.ReadAll(buf)
if err != nil {
return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
}
context, err = archive.Generate("Dockerfile", string(dockerfile))
} else {
context = ioutil.NopCloser(buf)
}
} else if utils.IsURL(cmd.Arg(0)) && (!utils.IsGIT(cmd.Arg(0)) || !hasGit) {
isRemote = true
} else {
root := cmd.Arg(0)
if utils.IsGIT(root) {
remoteURL := cmd.Arg(0)
if !strings.HasPrefix(remoteURL, "git://") && !strings.HasPrefix(remoteURL, "git@") && !utils.IsURL(remoteURL) {
remoteURL = "https://" + remoteURL
}
root, err = ioutil.TempDir("", "docker-build-git")
if err != nil {
return err
}
defer os.RemoveAll(root)
if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
}
}
if _, err := os.Stat(root); err != nil {
return err
}
filename := path.Join(root, "Dockerfile")
if _, err = os.Stat(filename); os.IsNotExist(err) {
return fmt.Errorf("no Dockerfile found in %s", cmd.Arg(0))
}
if err = utils.ValidateContextDirectory(root); err != nil {
return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
}
options := &archive.TarOptions{
Compression: archive.Uncompressed,
}
if ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore")); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("Error reading .dockerignore: '%s'", err)
} else if err == nil {
for _, pattern := range strings.Split(string(ignore), "\n") {
ok, err := filepath.Match(pattern, "Dockerfile")
if err != nil {
utils.Errorf("Bad .dockerignore pattern: '%s', error: %s", pattern, err)
continue
}
if ok {
return fmt.Errorf("Dockerfile was excluded by .dockerignore pattern '%s'", pattern)
}
options.Excludes = append(options.Excludes, pattern)
}
}
context, err = archive.TarWithOptions(root, options)
}
var body io.Reader
// Setup an upload progress bar
2013-08-12 18:53:06 +01:00
// FIXME: ProgressReader shouldn't be this annoying to use
if context != nil {
sf := utils.NewStreamFormatter(false)
body = utils.ProgressReader(context, 0, cli.err, sf, true, "", "Sending build context to Docker daemon")
}
// Send the build context
v := &url.Values{}
//Check if the given image name can be resolved
if *tag != "" {
repository, _ := utils.ParseRepositoryTag(*tag)
if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
return err
}
}
v.Set("t", *tag)
if *suppressOutput {
v.Set("q", "1")
}
if isRemote {
v.Set("remote", cmd.Arg(0))
}
if *noCache {
v.Set("nocache", "1")
}
if *rm {
v.Set("rm", "1")
} else {
v.Set("rm", "0")
}
if *forceRm {
v.Set("forcerm", "1")
}
cli.LoadConfigFile()
headers := http.Header(make(map[string][]string))
2013-12-06 14:27:10 -08:00
buf, err := json.Marshal(cli.configFile)
if err != nil {
return err
}
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
2013-12-06 14:27:10 -08:00
if context != nil {
headers.Set("Content-Type", "application/tar")
2013-06-18 18:59:56 +00:00
}
err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers)
if jerr, ok := err.(*utils.JSONError); ok {
// If no error code is set, default to 1
if jerr.Code == 0 {
jerr.Code = 1
}
return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
}
return err
2013-04-24 11:03:01 -07:00
}
// 'docker login': login / register a user to registry service.
func (cli *DockerCli) CmdLogin(args ...string) error {
cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
var username, password, email string
cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
2013-06-22 00:37:02 +00:00
err := cmd.Parse(args)
2013-05-06 13:34:31 +02:00
if err != nil {
return nil
2013-05-06 13:34:31 +02:00
}
serverAddress := registry.IndexServerAddress()
if len(cmd.Args()) > 0 {
serverAddress = cmd.Arg(0)
}
promptDefault := func(prompt string, configDefault string) {
if configDefault == "" {
fmt.Fprintf(cli.out, "%s: ", prompt)
} else {
fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
}
}
readInput := func(in io.Reader, out io.Writer) string {
reader := bufio.NewReader(in)
line, _, err := reader.ReadLine()
if err != nil {
fmt.Fprintln(out, err.Error())
os.Exit(1)
}
return string(line)
}
cli.LoadConfigFile()
authconfig, ok := cli.configFile.Configs[serverAddress]
if !ok {
authconfig = registry.AuthConfig{}
}
if username == "" {
2013-07-27 10:17:57 -07:00
promptDefault("Username", authconfig.Username)
username = readInput(cli.in, cli.out)
2013-06-21 10:00:25 +00:00
if username == "" {
username = authconfig.Username
2013-06-21 10:00:25 +00:00
}
}
if username != authconfig.Username {
if password == "" {
oldState, _ := term.SaveState(cli.terminalFd)
2013-07-01 12:31:16 +00:00
fmt.Fprintf(cli.out, "Password: ")
2013-08-17 21:29:37 -07:00
term.DisableEcho(cli.terminalFd, oldState)
password = readInput(cli.in, cli.out)
fmt.Fprint(cli.out, "\n")
term.RestoreTerminal(cli.terminalFd, oldState)
2013-06-21 10:00:25 +00:00
if password == "" {
return fmt.Errorf("Error : Password Required")
}
}
if email == "" {
2013-07-27 10:17:57 -07:00
promptDefault("Email", authconfig.Email)
email = readInput(cli.in, cli.out)
2013-06-21 10:00:25 +00:00
if email == "" {
email = authconfig.Email
2013-06-21 10:00:25 +00:00
}
}
} else {
password = authconfig.Password
email = authconfig.Email
}
authconfig.Username = username
authconfig.Password = password
authconfig.Email = email
authconfig.ServerAddress = serverAddress
cli.configFile.Configs[serverAddress] = authconfig
2013-05-06 13:34:31 +02:00
stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
2013-06-14 13:38:51 +00:00
if statusCode == 401 {
delete(cli.configFile.Configs, serverAddress)
registry.SaveConfig(cli.configFile)
2013-06-14 13:38:51 +00:00
return err
}
if err != nil {
2013-05-06 13:34:31 +02:00
return err
}
var out2 engine.Env
err = out2.Decode(stream)
2013-05-06 13:34:31 +02:00
if err != nil {
cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME"))
2013-05-06 13:34:31 +02:00
return err
}
registry.SaveConfig(cli.configFile)
if out2.Get("Status") != "" {
fmt.Fprintf(cli.out, "%s\n", out2.Get("Status"))
}
return nil
}
// 'docker wait': block until a container stops
func (cli *DockerCli) CmdWait(args ...string) error {
cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
var encounteredError error
for _, name := range cmd.Args() {
status, err := waitForExit(cli, name)
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to wait one or more containers")
} else {
fmt.Fprintf(cli.out, "%d\n", status)
}
}
return encounteredError
}
2013-03-12 17:34:15 -07:00
// 'docker version': show version information
func (cli *DockerCli) CmdVersion(args ...string) error {
cmd := cli.Subcmd("version", "", "Show the Docker version information.")
2013-04-22 18:17:47 +02:00
if err := cmd.Parse(args); err != nil {
return nil
2013-04-18 21:08:33 -07:00
}
2013-04-22 18:17:47 +02:00
if cmd.NArg() > 0 {
cmd.Usage()
return nil
}
if dockerversion.VERSION != "" {
fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
2013-08-30 00:46:43 +00:00
}
fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION)
fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
if dockerversion.GITCOMMIT != "" {
fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
2013-08-19 12:05:47 +00:00
}
body, _, err := readBody(cli.call("GET", "/version", nil, false))
2013-04-22 18:17:47 +02:00
if err != nil {
return err
}
2013-04-22 18:17:47 +02:00
out := engine.NewOutput()
remoteVersion, err := out.AddEnv()
2013-04-22 18:17:47 +02:00
if err != nil {
utils.Errorf("Error reading remote version: %s\n", err)
2013-04-22 18:17:47 +02:00
return err
}
if _, err := out.Write(body); err != nil {
utils.Errorf("Error reading remote version: %s\n", err)
return err
}
out.Close()
fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version"))
if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" {
fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion)
}
fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion"))
fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit"))
2013-03-12 17:34:15 -07:00
return nil
}
// 'docker info': display system-wide information.
func (cli *DockerCli) CmdInfo(args ...string) error {
cmd := cli.Subcmd("info", "", "Display system-wide information")
if err := cmd.Parse(args); err != nil {
return nil
}
2013-03-12 08:59:32 -07:00
if cmd.NArg() > 0 {
cmd.Usage()
return nil
}
body, _, err := readBody(cli.call("GET", "/info", nil, false))
2013-04-22 18:17:47 +02:00
if err != nil {
return err
}
2013-12-11 10:35:21 -08:00
out := engine.NewOutput()
remoteInfo, err := out.AddEnv()
if err != nil {
2013-04-22 18:17:47 +02:00
return err
}
2013-12-11 10:35:21 -08:00
if _, err := out.Write(body); err != nil {
utils.Errorf("Error reading remote info: %s\n", err)
return err
}
out.Close()
fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver"))
2013-12-12 13:35:50 -08:00
var driverStatus [][2]string
if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
return err
}
for _, pair := range driverStatus {
fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
}
fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
2013-12-11 10:35:21 -08:00
if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
2013-12-11 10:35:21 -08:00
fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
}
if initPath := remoteInfo.Get("InitPath"); initPath != "" {
fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
}
if len(remoteInfo.GetList("Sockets")) != 0 {
fmt.Fprintf(cli.out, "Sockets: %v\n", remoteInfo.GetList("Sockets"))
}
}
2013-07-22 16:09:11 -04:00
2013-12-11 10:35:21 -08:00
if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
cli.LoadConfigFile()
2013-12-11 10:35:21 -08:00
u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username
2013-07-22 14:42:31 -04:00
if len(u) > 0 {
fmt.Fprintf(cli.out, "Username: %v\n", u)
2013-12-11 10:35:21 -08:00
fmt.Fprintf(cli.out, "Registry: %v\n", remoteInfo.GetList("IndexServerAddress"))
2013-07-22 14:42:31 -04:00
}
}
2013-12-11 10:35:21 -08:00
if !remoteInfo.GetBool("MemoryLimit") {
fmt.Fprintf(cli.err, "WARNING: No memory limit support\n")
}
2013-12-11 10:35:21 -08:00
if !remoteInfo.GetBool("SwapLimit") {
fmt.Fprintf(cli.err, "WARNING: No swap limit support\n")
}
2013-12-11 10:35:21 -08:00
if !remoteInfo.GetBool("IPv4Forwarding") {
fmt.Fprintf(cli.err, "WARNING: IPv4 forwarding is disabled.\n")
}
return nil
}
func (cli *DockerCli) CmdStop(args ...string) error {
cmd := cli.Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period")
nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
2013-04-22 18:17:47 +02:00
2013-05-02 18:36:23 +02:00
v := url.Values{}
v.Set("t", strconv.Itoa(*nSeconds))
var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
2013-04-22 18:17:47 +02:00
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
} else {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
return encounteredError
}
func (cli *DockerCli) CmdRestart(args ...string) error {
cmd := cli.Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container")
nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default is 10 seconds.")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
2013-04-22 18:17:47 +02:00
2013-05-02 18:36:23 +02:00
v := url.Values{}
v.Set("t", strconv.Itoa(*nSeconds))
var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
2013-04-22 18:17:47 +02:00
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
} else {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
return encounteredError
}
2013-10-29 11:59:08 -07:00
func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
sigc := make(chan os.Signal, 1)
signal.CatchAll(sigc)
go func() {
for s := range sigc {
if s == syscall.SIGCHLD {
continue
}
var sig string
for sigStr, sigN := range signal.SignalMap {
if sigN == s {
sig = sigStr
break
}
}
if sig == "" {
utils.Errorf("Unsupported signal: %d. Discarding.", s)
}
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
utils.Debugf("Error sending signal: %s", err)
}
}
}()
2013-10-29 11:59:08 -07:00
return sigc
}
func (cli *DockerCli) CmdStart(args ...string) error {
var (
cErr chan error
tty bool
cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's STDOUT and STDERR and forward all signals to the process")
openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
)
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
2013-04-22 18:17:47 +02:00
if *attach || *openStdin {
if cmd.NArg() > 1 {
return fmt.Errorf("You cannot start and attach multiple containers at once.")
}
steam, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
if err != nil {
return err
}
env := engine.Env{}
if err := env.Decode(steam); err != nil {
return err
}
config := env.GetSubEnv("Config")
tty = config.GetBool("Tty")
if !tty {
2013-10-29 11:59:08 -07:00
sigc := cli.forwardAllSignals(cmd.Arg(0))
defer signal.StopCatch(sigc)
}
2013-10-25 17:17:33 -07:00
var in io.ReadCloser
v := url.Values{}
v.Set("stream", "1")
if *openStdin && config.GetBool("OpenStdin") {
v.Set("stdin", "1")
2013-10-25 17:17:33 -07:00
in = cli.in
}
v.Set("stdout", "1")
v.Set("stderr", "1")
cErr = utils.Go(func() error {
return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil)
})
}
var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
2013-04-22 18:17:47 +02:00
if err != nil {
if !*attach || !*openStdin {
fmt.Fprintf(cli.err, "%s\n", err)
}
encounteredError = fmt.Errorf("Error: failed to start one or more containers")
} else {
if !*attach || !*openStdin {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
}
if encounteredError != nil {
if *openStdin || *attach {
cli.in.Close()
<-cErr
}
return encounteredError
}
if *openStdin || *attach {
if tty && cli.isTerminal {
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
utils.Errorf("Error monitoring TTY size: %s\n", err)
}
}
return <-cErr
}
return nil
}
func (cli *DockerCli) CmdUnpause(args ...string) error {
cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
var encounteredError error
for _, name := range cmd.Args() {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
} else {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
return encounteredError
}
func (cli *DockerCli) CmdPause(args ...string) error {
cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
var encounteredError error
for _, name := range cmd.Args() {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
} else {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
return encounteredError
}
func (cli *DockerCli) CmdInspect(args ...string) error {
cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image")
tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
if err := cmd.Parse(args); err != nil {
return nil
}
2013-06-06 15:22:54 +00:00
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
var tmpl *template.Template
if *tmplStr != "" {
var err error
if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
return &utils.StatusError{StatusCode: 64,
Status: "Template parsing error: " + err.Error()}
}
}
indented := new(bytes.Buffer)
indented.WriteByte('[')
2013-10-17 23:45:18 +00:00
status := 0
for _, name := range cmd.Args() {
obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
if err != nil {
obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
2013-06-06 15:22:54 +00:00
if err != nil {
2013-10-31 18:44:16 -07:00
if strings.Contains(err.Error(), "No such") {
fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
} else {
fmt.Fprintf(cli.err, "%s", err)
}
2013-10-17 23:45:18 +00:00
status = 1
2013-06-06 15:22:54 +00:00
continue
}
}
2013-05-09 03:46:39 +02:00
if tmpl == nil {
if err = json.Indent(indented, obj, "", " "); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
status = 1
continue
}
} else {
// Has template, will render
var value interface{}
if err := json.Unmarshal(obj, &value); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
status = 1
continue
}
if err := tmpl.Execute(cli.out, value); err != nil {
return err
}
cli.out.Write([]byte{'\n'})
2013-06-06 15:22:54 +00:00
}
indented.WriteString(",")
}
if indented.Len() > 1 {
2013-10-31 18:44:16 -07:00
// Remove trailing ','
indented.Truncate(indented.Len() - 1)
}
indented.WriteByte(']')
if tmpl == nil {
if _, err := io.Copy(cli.out, indented); err != nil {
return err
}
}
2013-10-17 23:45:18 +00:00
if status != 0 {
return &utils.StatusError{StatusCode: status}
2013-10-17 23:45:18 +00:00
}
return nil
}
2013-07-01 15:19:42 +00:00
func (cli *DockerCli) CmdTop(args ...string) error {
cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container")
if err := cmd.Parse(args); err != nil {
return nil
}
2013-07-19 10:06:32 +00:00
if cmd.NArg() == 0 {
cmd.Usage()
return nil
}
2013-07-19 10:06:32 +00:00
val := url.Values{}
if cmd.NArg() > 1 {
val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
}
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
if err != nil {
return err
}
var procs engine.Env
if err := procs.Decode(stream); err != nil {
return err
}
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
fmt.Fprintln(w, strings.Join(procs.GetList("Titles"), "\t"))
processes := [][]string{}
if err := procs.GetJson("Processes", &processes); err != nil {
return err
}
for _, proc := range processes {
2013-07-19 10:06:32 +00:00
fmt.Fprintln(w, strings.Join(proc, "\t"))
}
w.Flush()
return nil
}
func (cli *DockerCli) CmdPort(args ...string) error {
cmd := cli.Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port that is NAT-ed to PRIVATE_PORT")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 2 {
cmd.Usage()
return nil
}
2013-05-08 18:06:43 +02:00
var (
port = cmd.Arg(1)
proto = "tcp"
parts = strings.SplitN(port, "/", 2)
)
2013-07-11 20:30:08 +02:00
if len(parts) == 2 && len(parts[1]) != 0 {
port = parts[0]
2013-10-23 16:41:59 -07:00
proto = parts[1]
2013-07-11 20:30:08 +02:00
}
steam, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
if err != nil {
return err
}
env := engine.Env{}
if err := env.Decode(steam); err != nil {
return err
}
ports := nat.PortMap{}
if err := env.GetSubEnv("NetworkSettings").GetJson("Ports", &ports); err != nil {
return err
}
2013-05-08 18:06:43 +02:00
if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
for _, frontend := range frontends {
fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
2013-10-23 16:41:59 -07:00
}
return nil
}
return fmt.Errorf("Error: No public port '%s' published for %s", cmd.Arg(1), cmd.Arg(0))
}
2013-04-11 18:46:47 +02:00
// 'docker rmi IMAGE' removes all images with the name IMAGE
func (cli *DockerCli) CmdRmi(args ...string) error {
var (
cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
)
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
2013-03-13 11:14:37 -07:00
cmd.Usage()
return nil
}
2013-04-22 18:17:47 +02:00
v := url.Values{}
if *force {
v.Set("force", "1")
}
if *noprune {
v.Set("noprune", "1")
}
var encounteredError error
2013-03-13 11:14:37 -07:00
for _, name := range cmd.Args() {
body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
2013-04-22 18:17:47 +02:00
} else {
outs := engine.NewTable("Created", 0)
if _, err := outs.ReadListFrom(body); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
continue
}
for _, out := range outs.Data {
if out.Get("Deleted") != "" {
fmt.Fprintf(cli.out, "Deleted: %s\n", out.Get("Deleted"))
} else {
fmt.Fprintf(cli.out, "Untagged: %s\n", out.Get("Untagged"))
}
}
2013-03-13 11:14:37 -07:00
}
}
return encounteredError
2013-03-13 11:14:37 -07:00
}
func (cli *DockerCli) CmdHistory(args ...string) error {
cmd := cli.Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image")
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
2013-10-18 18:39:40 -05:00
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
2013-04-22 18:17:47 +02:00
body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
if err != nil {
return err
}
2013-04-22 18:17:47 +02:00
outs := engine.NewTable("Created", 0)
if _, err := outs.ReadListFrom(body); err != nil {
return err
}
2013-10-18 18:39:40 -05:00
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
2013-10-18 18:39:40 -05:00
if !*quiet {
fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE")
2013-10-18 18:39:40 -05:00
}
2013-04-22 18:17:47 +02:00
for _, out := range outs.Data {
outID := out.Get("Id")
2013-10-18 18:39:40 -05:00
if !*quiet {
if *noTrunc {
fmt.Fprintf(w, "%s\t", outID)
2013-10-18 18:39:40 -05:00
} else {
fmt.Fprintf(w, "%s\t", utils.TruncateID(outID))
2013-10-18 18:39:40 -05:00
}
fmt.Fprintf(w, "%s ago\t", units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))))
2013-10-18 18:39:40 -05:00
if *noTrunc {
fmt.Fprintf(w, "%s\t", out.Get("CreatedBy"))
2013-10-18 18:39:40 -05:00
} else {
fmt.Fprintf(w, "%s\t", utils.Trunc(out.Get("CreatedBy"), 45))
2013-10-18 18:39:40 -05:00
}
fmt.Fprintf(w, "%s\n", units.HumanSize(out.GetInt64("Size")))
2013-10-18 18:39:40 -05:00
} else {
if *noTrunc {
fmt.Fprintln(w, outID)
2013-10-18 18:39:40 -05:00
} else {
fmt.Fprintln(w, utils.TruncateID(outID))
2013-10-18 18:39:40 -05:00
}
}
2013-04-22 18:17:47 +02:00
}
w.Flush()
return nil
}
func (cli *DockerCli) CmdRm(args ...string) error {
cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
if err := cmd.Parse(args); err != nil {
return nil
}
2013-04-11 16:27:01 +02:00
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
2013-05-06 11:52:15 +02:00
val := url.Values{}
2013-04-12 09:23:57 -07:00
if *v {
2013-05-06 11:52:15 +02:00
val.Set("v", "1")
}
if *link {
val.Set("link", "1")
}
if *force {
val.Set("force", "1")
}
var encounteredError error
2013-05-06 11:52:15 +02:00
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
2013-04-22 18:17:47 +02:00
if err != nil {
2013-06-25 10:39:11 -07:00
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
2013-04-22 18:17:47 +02:00
} else {
2013-06-25 10:39:11 -07:00
fmt.Fprintf(cli.out, "%s\n", name)
2013-04-12 09:23:57 -07:00
}
}
return encounteredError
}
// 'docker kill NAME' kills a running container
func (cli *DockerCli) CmdKill(args ...string) error {
cmd := cli.Subcmd("kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal")
signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
2013-04-22 18:17:47 +02:00
var encounteredError error
for _, name := range cmd.Args() {
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
2013-06-25 10:39:11 -07:00
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
2013-04-22 18:17:47 +02:00
} else {
2013-06-25 10:39:11 -07:00
fmt.Fprintf(cli.out, "%s\n", name)
}
}
return encounteredError
}
func (cli *DockerCli) CmdImport(args ...string) error {
cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
2013-11-07 17:30:51 -08:00
var src, repository, tag string
if cmd.NArg() == 3 {
fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' as been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
src, repository, tag = cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
} else {
src = cmd.Arg(0)
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
}
v := url.Values{}
if repository != "" {
//Check if the given image name can be resolved
if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
return err
}
}
v.Set("repo", repository)
v.Set("tag", tag)
v.Set("fromSrc", src)
2013-09-15 18:40:29 -07:00
var in io.Reader
if src == "-" {
in = cli.in
}
2013-09-15 18:40:29 -07:00
return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil)
}
func (cli *DockerCli) CmdPush(args ...string) error {
cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry")
if err := cmd.Parse(args); err != nil {
return nil
}
2013-05-06 13:34:31 +02:00
name := cmd.Arg(0)
2013-03-22 06:38:54 -07:00
2013-05-06 13:34:31 +02:00
if name == "" {
cmd.Usage()
return nil
}
cli.LoadConfigFile()
remote, tag := utils.ParseRepositoryTag(name)
// Resolve the Repository name from fqn to hostname + name
hostname, _, err := registry.ResolveRepositoryName(remote)
if err != nil {
return err
}
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(hostname)
// If we're not using a custom registry, we know the restrictions
// applied to repository names and can warn the user in advance.
// Custom repositories can have different rules, and we must also
// allow pushing by image ID.
2013-05-06 13:34:31 +02:00
if len(strings.SplitN(name, "/", 2)) == 1 {
username := cli.configFile.Configs[registry.IndexServerAddress()].Username
if username == "" {
username = "<user>"
}
return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
2013-03-22 06:38:54 -07:00
}
v := url.Values{}
v.Set("tag", tag)
push := func(authConfig registry.AuthConfig) error {
buf, err := json.Marshal(authConfig)
if err != nil {
return err
}
registryAuthHeader := []string{
2013-08-30 22:49:37 +02:00
base64.URLEncoding.EncodeToString(buf),
}
return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
"X-Registry-Auth": registryAuthHeader,
})
2013-03-22 06:38:54 -07:00
}
if err := push(authConfig); err != nil {
if strings.Contains(err.Error(), "Status 401") {
2013-08-13 13:51:49 +00:00
fmt.Fprintln(cli.out, "\nPlease login prior to push:")
if err := cli.CmdLogin(hostname); err != nil {
2013-08-13 13:51:49 +00:00
return err
}
authConfig := cli.configFile.ResolveAuthConfig(hostname)
return push(authConfig)
}
return err
}
return nil
}
func (cli *DockerCli) CmdPull(args ...string) error {
cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry")
tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in a repository")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
2013-03-22 01:25:27 -07:00
remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0))
if *tag == "" {
*tag = parsedTag
}
// Resolve the Repository name from fqn to hostname + name
hostname, _, err := registry.ResolveRepositoryName(remote)
if err != nil {
return err
}
cli.LoadConfigFile()
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(hostname)
v := url.Values{}
2013-05-07 19:23:50 +02:00
v.Set("fromImage", remote)
v.Set("tag", *tag)
2013-03-22 01:25:27 -07:00
pull := func(authConfig registry.AuthConfig) error {
buf, err := json.Marshal(authConfig)
if err != nil {
return err
}
registryAuthHeader := []string{
2013-08-30 22:49:37 +02:00
base64.URLEncoding.EncodeToString(buf),
}
return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
"X-Registry-Auth": registryAuthHeader,
})
}
if err := pull(authConfig); err != nil {
if strings.Contains(err.Error(), "Status 401") {
fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
if err := cli.CmdLogin(hostname); err != nil {
return err
}
authConfig := cli.configFile.ResolveAuthConfig(hostname)
return pull(authConfig)
}
return err
}
return nil
}
func (cli *DockerCli) CmdImages(args ...string) error {
cmd := cli.Subcmd("images", "[OPTIONS] [NAME]", "List images")
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate image layers)")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
// FIXME: --viz and --tree are deprecated. Remove them in a future version.
flViz := cmd.Bool([]string{"#v", "#viz", "#-viz"}, false, "Output graph in graphviz format")
flTree := cmd.Bool([]string{"#t", "#tree", "#-tree"}, false, "Output graph in tree format")
var flFilter opts.ListOpts
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() > 1 {
cmd.Usage()
return nil
}
2013-04-30 22:39:48 -07:00
// Consolidate all filter flags, and sanity check them early.
// They'll get process in the daemon/server.
imageFilterArgs := filters.Args{}
for _, f := range flFilter.GetAll() {
var err error
imageFilterArgs, err = filters.ParseFlag(f, imageFilterArgs)
if err != nil {
return err
}
}
matchName := cmd.Arg(0)
// FIXME: --viz and --tree are deprecated. Remove them in a future version.
if *flViz || *flTree {
v := url.Values{
"all": []string{"1"},
}
if len(imageFilterArgs) > 0 {
filterJson, err := filters.ToParam(imageFilterArgs)
if err != nil {
return err
}
v.Set("filters", filterJson)
}
body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
2013-10-09 14:38:39 +00:00
if err != nil {
return err
}
outs := engine.NewTable("Created", 0)
if _, err := outs.ReadListFrom(body); err != nil {
2013-10-09 14:38:39 +00:00
return err
}
2013-11-12 12:25:35 -08:00
var (
printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
startImage *engine.Env
2013-11-12 12:25:35 -08:00
roots = engine.NewTable("Created", outs.Len())
byParent = make(map[string]*engine.Table)
2013-11-12 12:25:35 -08:00
)
2013-10-09 14:38:39 +00:00
for _, image := range outs.Data {
if image.Get("ParentId") == "" {
roots.Add(image)
2013-10-09 14:38:39 +00:00
} else {
if children, exists := byParent[image.Get("ParentId")]; exists {
children.Add(image)
2013-10-09 14:38:39 +00:00
} else {
byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
byParent[image.Get("ParentId")].Add(image)
2013-10-09 14:38:39 +00:00
}
}
if matchName != "" {
if matchName == image.Get("Id") || matchName == utils.TruncateID(image.Get("Id")) {
2013-10-09 14:38:39 +00:00
startImage = image
}
for _, repotag := range image.GetList("RepoTags") {
if repotag == matchName {
2013-10-09 14:38:39 +00:00
startImage = image
}
}
}
}
if *flViz {
fmt.Fprintf(cli.out, "digraph docker {\n")
printNode = (*DockerCli).printVizNode
2013-10-09 14:38:39 +00:00
} else {
printNode = (*DockerCli).printTreeNode
}
if startImage != nil {
root := engine.NewTable("Created", 1)
root.Add(startImage)
cli.WalkTree(*noTrunc, root, byParent, "", printNode)
} else if matchName == "" {
cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
}
if *flViz {
fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
2013-10-09 14:38:39 +00:00
}
2013-04-30 22:39:48 -07:00
} else {
v := url.Values{}
if len(imageFilterArgs) > 0 {
filterJson, err := filters.ToParam(imageFilterArgs)
if err != nil {
return err
}
v.Set("filters", filterJson)
}
2013-04-30 22:39:48 -07:00
if cmd.NArg() == 1 {
// FIXME rename this parameter, to not be confused with the filters flag
v.Set("filter", matchName)
2013-04-30 22:39:48 -07:00
}
2013-05-07 19:23:50 +02:00
if *all {
v.Set("all", "1")
2013-04-30 22:39:48 -07:00
}
2013-04-22 18:17:47 +02:00
body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
2013-04-30 22:39:48 -07:00
if err != nil {
return err
}
2013-04-22 18:17:47 +02:00
outs := engine.NewTable("Created", 0)
if _, err := outs.ReadListFrom(body); err != nil {
2013-05-07 19:23:50 +02:00
return err
}
2013-05-07 19:23:50 +02:00
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
2013-04-22 18:17:47 +02:00
if !*quiet {
fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
}
2013-04-22 18:17:47 +02:00
for _, out := range outs.Data {
for _, repotag := range out.GetList("RepoTags") {
repo, tag := utils.ParseRepositoryTag(repotag)
outID := out.Get("Id")
2013-10-06 05:44:04 +00:00
if !*noTrunc {
outID = utils.TruncateID(outID)
2013-10-06 05:44:04 +00:00
}
2013-10-06 05:44:04 +00:00
if !*quiet {
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(out.GetInt64("VirtualSize")))
2013-05-22 13:41:29 +00:00
} else {
fmt.Fprintln(w, outID)
2013-05-22 13:41:29 +00:00
}
}
}
2013-05-07 19:23:50 +02:00
2013-04-30 22:39:48 -07:00
if !*quiet {
w.Flush()
}
}
return nil
}
// FIXME: --viz and --tree are deprecated. Remove them in a future version.
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)) {
length := images.Len()
if length > 1 {
for index, image := range images.Data {
2013-10-09 14:38:39 +00:00
if index+1 == length {
printNode(cli, noTrunc, image, prefix+"└─")
if subimages, exists := byParent[image.Get("Id")]; exists {
cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode)
2013-10-09 14:38:39 +00:00
}
} else {
printNode(cli, noTrunc, image, prefix+"\u251C─")
if subimages, exists := byParent[image.Get("Id")]; exists {
cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
2013-10-09 14:38:39 +00:00
}
}
}
} else {
for _, image := range images.Data {
printNode(cli, noTrunc, image, prefix+"└─")
if subimages, exists := byParent[image.Get("Id")]; exists {
cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode)
2013-10-09 14:38:39 +00:00
}
}
}
}
// FIXME: --viz and --tree are deprecated. Remove them in a future version.
func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
var (
imageID string
parentID string
)
if noTrunc {
imageID = image.Get("Id")
parentID = image.Get("ParentId")
} else {
imageID = utils.TruncateID(image.Get("Id"))
parentID = utils.TruncateID(image.Get("ParentId"))
}
if parentID == "" {
fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
} else {
fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
}
if image.GetList("RepoTags")[0] != "<none>:<none>" {
fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
}
}
// FIXME: --viz and --tree are deprecated. Remove them in a future version.
func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
2013-10-09 14:38:39 +00:00
var imageID string
if noTrunc {
imageID = image.Get("Id")
2013-10-09 14:38:39 +00:00
} else {
imageID = utils.TruncateID(image.Get("Id"))
2013-10-09 14:38:39 +00:00
}
fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(image.GetInt64("VirtualSize")))
if image.GetList("RepoTags")[0] != "<none>:<none>" {
fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
2013-10-09 14:38:39 +00:00
} else {
2013-10-27 23:42:09 +00:00
fmt.Fprint(cli.out, "\n")
2013-10-09 14:38:39 +00:00
}
}
func (cli *DockerCli) CmdPs(args ...string) error {
cmd := cli.Subcmd("ps", "[OPTIONS]", "List containers")
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
size := cmd.Bool([]string{"s", "-size"}, false, "Display sizes")
all := cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.")
since := cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.")
before := cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name, include non-running ones.")
last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.")
2013-04-22 18:17:47 +02:00
if err := cmd.Parse(args); err != nil {
return nil
}
2013-04-22 18:17:47 +02:00
v := url.Values{}
if *last == -1 && *nLatest {
*last = 1
}
if *all {
2013-04-26 15:08:33 +02:00
v.Set("all", "1")
2013-04-22 18:17:47 +02:00
}
if *last != -1 {
v.Set("limit", strconv.Itoa(*last))
2013-04-22 18:17:47 +02:00
}
2013-05-08 18:28:11 +02:00
if *since != "" {
v.Set("since", *since)
}
if *before != "" {
v.Set("before", *before)
}
2013-06-20 14:19:50 +00:00
if *size {
v.Set("size", "1")
}
body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
2013-04-22 18:17:47 +02:00
if err != nil {
return err
}
outs := engine.NewTable("Created", 0)
if _, err := outs.ReadListFrom(body); err != nil {
2013-04-22 18:17:47 +02:00
return err
2013-04-10 19:30:57 +02:00
}
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
2013-02-13 17:28:13 -08:00
if !*quiet {
fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
2013-06-20 14:19:50 +00:00
if *size {
fmt.Fprintln(w, "\tSIZE")
} else {
fmt.Fprint(w, "\n")
}
}
2013-04-22 18:17:47 +02:00
for _, out := range outs.Data {
var (
outID = out.Get("Id")
outNames = out.GetList("Names")
)
if !*noTrunc {
outID = utils.TruncateID(outID)
}
// Remove the leading / from the names
for i := 0; i < len(outNames); i++ {
outNames[i] = outNames[i][1:]
}
if !*quiet {
var (
outCommand = out.Get("Command")
ports = engine.NewTable("", 0)
)
if !*noTrunc {
outCommand = utils.Trunc(outCommand, 20)
2013-05-22 13:41:29 +00:00
}
ports.ReadListFrom([]byte(out.Get("Ports")))
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
2013-06-20 14:19:50 +00:00
if *size {
if out.GetInt("SizeRootFs") > 0 {
fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(out.GetInt64("SizeRw")), units.HumanSize(out.GetInt64("SizeRootFs")))
2013-06-20 14:19:50 +00:00
} else {
fmt.Fprintf(w, "%s\n", units.HumanSize(out.GetInt64("SizeRw")))
2013-06-20 14:19:50 +00:00
}
2013-05-13 15:10:26 +02:00
} else {
2013-06-20 14:19:50 +00:00
fmt.Fprint(w, "\n")
2013-05-13 15:10:26 +02:00
}
} else {
fmt.Fprintln(w, outID)
}
}
2013-04-22 18:17:47 +02:00
2013-02-13 17:28:13 -08:00
if !*quiet {
w.Flush()
}
return nil
}
func (cli *DockerCli) CmdCommit(args ...string) error {
cmd := cli.Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes")
flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\")")
Early deprecation warning for 'docker commit --run' Warn users of the planned deprecation of 'docker commit --run', and hide it from the docs and usage message. The option continues to work. Note that an alternative to 'commit --run' is being implemented but is not yet available. We are printing the warning anyway because on the basis that it never hurts to give more advance warning. The 'commit --run' flag is a leftover from the very early days of Docker, and has several problems: 1) It is very user unfriendly. You have to pass a literal json dict which is poorly documented and changes regularly (see PortSpecs vs ExposedPorts). The merge behavior is not clear and also changes regularly. it's not possible to unset a value. 2) It overlaps with the Dockerfile syntax. There are 2 ways to set a default command, expose a port or change an env variable. Some things can be done in a Dockerfile but not in --run. Some things can be done in --run but not in a Dockerfile. It would be better to push a single syntax, allow using it both in a file and via the command line, and make improvements in a single place. 3) It exposes data structures which should not be publicly exposed. There are several planned improvements to Docker which require moving around the content and schema of the various Config, Image and Container structures. The less of those we expose in public interfaces, the easier it is to move things around without a reverse compatibility nightmare. Docker-DCO-1.1-Signed-off-by: Solomon Hykes <solomon@docker.com> (github: shykes)
2014-04-08 09:49:48 -07:00
// FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands.
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")
if err := cmd.Parse(args); err != nil {
return nil
}
2013-11-07 17:30:51 -08:00
var (
name = cmd.Arg(0)
2013-11-07 17:30:51 -08:00
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
)
if name == "" || len(cmd.Args()) > 2 {
cmd.Usage()
return nil
}
//Check if the given image name can be resolved
if repository != "" {
if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
return err
}
}
2013-04-24 16:06:03 +02:00
v := url.Values{}
v.Set("container", name)
2013-04-24 16:06:03 +02:00
v.Set("repo", repository)
v.Set("tag", tag)
v.Set("comment", *flComment)
2013-05-02 18:36:23 +02:00
v.Set("author", *flAuthor)
if *flPause != true {
v.Set("pause", "0")
}
var (
config *runconfig.Config
env engine.Env
)
if *flConfig != "" {
config = &runconfig.Config{}
if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
return err
}
}
stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
if err != nil {
return err
2013-04-24 15:24:14 -07:00
}
if err := env.Decode(stream); err != nil {
return err
}
2013-04-24 16:06:03 +02:00
fmt.Fprintf(cli.out, "%s\n", env.Get("Id"))
return nil
}
2013-07-10 12:55:05 +00:00
func (cli *DockerCli) CmdEvents(args ...string) error {
cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server")
since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
2013-07-10 12:55:05 +00:00
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 0 {
cmd.Usage()
return nil
}
var (
v = url.Values{}
loc = time.FixedZone(time.Now().Zone())
)
var setTime = func(key, value string) {
format := "2006-01-02 15:04:05 -0700 MST"
if len(value) < len(format) {
format = format[:len(value)]
}
if t, err := time.ParseInLocation(format, value, loc); err == nil {
v.Set(key, strconv.FormatInt(t.Unix(), 10))
} else {
v.Set(key, value)
}
}
if *since != "" {
setTime("since", *since)
}
if *until != "" {
setTime("until", *until)
}
if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil {
2013-07-10 12:55:05 +00:00
return err
}
return nil
}
func (cli *DockerCli) CmdExport(args ...string) error {
cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT")
if err := cmd.Parse(args); err != nil {
return nil
}
2013-04-24 16:32:51 +02:00
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
2013-04-24 16:32:51 +02:00
if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil {
2013-04-24 16:32:51 +02:00
return err
}
return nil
}
func (cli *DockerCli) CmdDiff(args ...string) error {
cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
if err != nil {
return err
}
outs := engine.NewTable("", 0)
if _, err := outs.ReadListFrom(body); err != nil {
return err
}
for _, change := range outs.Data {
var kind string
switch change.GetInt("Kind") {
case archive.ChangeModify:
kind = "C"
case archive.ChangeAdd:
kind = "A"
case archive.ChangeDelete:
kind = "D"
}
fmt.Fprintf(cli.out, "%s %s\n", kind, change.Get("Path"))
}
return nil
}
func (cli *DockerCli) CmdLogs(args ...string) error {
var (
cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)")
)
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
2013-10-17 23:22:14 +00:00
name := cmd.Arg(0)
steam, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
2013-10-31 17:58:15 -07:00
if err != nil {
return err
}
env := engine.Env{}
if err := env.Decode(steam); err != nil {
2013-10-31 17:58:15 -07:00
return err
}
2013-04-22 18:17:47 +02:00
2013-12-03 06:18:01 +00:00
v := url.Values{}
v.Set("stdout", "1")
v.Set("stderr", "1")
if *times {
v.Set("timestamps", "1")
}
if *follow {
v.Set("follow", "1")
2013-12-03 06:18:01 +00:00
}
v.Set("tail", *tail)
2013-12-03 06:18:01 +00:00
return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil)
}
func (cli *DockerCli) CmdAttach(args ...string) error {
var (
cmd = cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container")
noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signals to the process (even in non-TTY mode). SIGCHLD is not proxied.")
)
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
name := cmd.Arg(0)
stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
if err != nil {
return err
}
env := engine.Env{}
if err := env.Decode(stream); err != nil {
return err
2013-04-19 14:18:03 -07:00
}
if !env.GetSubEnv("State").GetBool("Running") {
return fmt.Errorf("You cannot attach to a stopped container, start it first")
}
var (
config = env.GetSubEnv("Config")
tty = config.GetBool("Tty")
)
if tty && cli.isTerminal {
2013-06-24 14:59:37 -07:00
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
utils.Debugf("Error monitoring TTY size: %s", err)
2013-06-24 14:59:37 -07:00
}
2013-06-13 12:57:35 -07:00
}
2013-10-25 17:17:33 -07:00
var in io.ReadCloser
2013-05-02 05:07:06 +02:00
v := url.Values{}
v.Set("stream", "1")
if !*noStdin && config.GetBool("OpenStdin") {
v.Set("stdin", "1")
2013-10-25 17:17:33 -07:00
in = cli.in
}
2013-05-29 11:40:54 +00:00
v.Set("stdout", "1")
v.Set("stderr", "1")
if *proxy && !tty {
2013-10-29 11:59:08 -07:00
sigc := cli.forwardAllSignals(cmd.Arg(0))
defer signal.StopCatch(sigc)
}
if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil); err != nil {
return err
}
_, status, err := getExitCode(cli, cmd.Arg(0))
if err != nil {
return err
}
if status != 0 {
return &utils.StatusError{StatusCode: status}
}
2013-04-26 15:08:33 +02:00
return nil
}
func (cli *DockerCli) CmdSearch(args ...string) error {
cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars")
2013-05-07 03:49:08 -07:00
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
2013-05-07 20:59:04 +02:00
2013-05-07 20:37:35 +02:00
v := url.Values{}
2013-05-07 20:59:04 +02:00
v.Set("term", cmd.Arg(0))
body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
2013-05-07 20:59:04 +02:00
if err != nil {
return err
}
outs := engine.NewTable("star_count", 0)
if _, err := outs.ReadListFrom(body); err != nil {
2013-05-07 03:49:08 -07:00
return err
}
w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
for _, out := range outs.Data {
if ((*automated || *trusted) && (!out.GetBool("is_trusted") && !out.GetBool("is_automated"))) || (*stars > out.GetInt("star_count")) {
2013-10-31 19:21:35 -07:00
continue
}
desc := strings.Replace(out.Get("description"), "\n", " ", -1)
desc = strings.Replace(desc, "\r", " ", -1)
2013-11-04 15:34:51 -08:00
if !*noTrunc && len(desc) > 45 {
desc = utils.Trunc(desc, 42) + "..."
}
fmt.Fprintf(w, "%s\t%s\t%d\t", out.Get("name"), desc, out.GetInt("star_count"))
if out.GetBool("is_official") {
fmt.Fprint(w, "[OK]")
}
fmt.Fprint(w, "\t")
if out.GetBool("is_automated") || out.GetBool("is_trusted") {
fmt.Fprint(w, "[OK]")
}
fmt.Fprint(w, "\n")
2013-05-07 03:49:08 -07:00
}
w.Flush()
return nil
}
2013-02-28 11:52:22 -08:00
// Ports type - Used to parse multiple -p flags
type ports []int
func (cli *DockerCli) CmdTag(args ...string) error {
cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository")
force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 2 {
cmd.Usage()
return nil
}
var (
2013-11-07 17:30:51 -08:00
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
v = url.Values{}
)
//Check if the given image name can be resolved
if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
return err
}
v.Set("repo", repository)
v.Set("tag", tag)
if *force {
2013-04-26 15:08:33 +02:00
v.Set("force", "1")
}
if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
return err
}
return nil
}
func (cli *DockerCli) CmdRun(args ...string) error {
// FIXME: just use runconfig.Parse already
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
if err != nil {
return err
}
if config.Image == "" {
cmd.Usage()
return nil
}
2013-11-22 12:14:34 -08:00
// Retrieve relevant client-side config
var (
flName = cmd.Lookup("name")
flRm = cmd.Lookup("rm")
flSigProxy = cmd.Lookup("sig-proxy")
autoRemove, _ = strconv.ParseBool(flRm.Value.String())
sigProxy, _ = strconv.ParseBool(flSigProxy.Value.String())
)
2013-09-26 14:52:37 -07:00
2013-11-22 12:14:34 -08:00
// Disable sigProxy in case on TTY
if config.Tty {
sigProxy = false
}
2013-11-22 12:14:34 -08:00
var containerIDFile io.WriteCloser
if len(hostConfig.ContainerIDFile) > 0 {
2013-11-22 12:14:34 -08:00
if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil {
return fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
}
2013-11-22 12:14:34 -08:00
if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
return fmt.Errorf("Failed to create the container ID file: %s", err)
}
defer func() {
containerIDFile.Close()
var (
cidFileInfo os.FileInfo
err error
)
if cidFileInfo, err = os.Stat(hostConfig.ContainerIDFile); err != nil {
return
}
if cidFileInfo.Size() == 0 {
if err := os.Remove(hostConfig.ContainerIDFile); err != nil {
fmt.Printf("failed to remove Container ID file '%s': %s \n", hostConfig.ContainerIDFile, err)
}
}
}()
}
2013-11-22 12:14:34 -08:00
containerValues := url.Values{}
2013-11-22 12:14:34 -08:00
if name := flName.Value.String(); name != "" {
containerValues.Set("name", name)
}
2013-05-02 05:07:06 +02:00
//create the container
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false)
2013-05-02 05:07:06 +02:00
//if image not found try to pull it
if statusCode == 404 {
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
2013-08-17 20:03:54 -07:00
v := url.Values{}
repos, tag := utils.ParseRepositoryTag(config.Image)
// pull only the image tagged 'latest' if no tag was specified
if tag == "" {
tag = "latest"
}
v.Set("fromImage", repos)
v.Set("tag", tag)
// Resolve the Repository name from fqn to hostname + name
hostname, _, err := registry.ResolveRepositoryName(repos)
if err != nil {
return err
}
// Load the auth config file, to be able to pull the image
cli.LoadConfigFile()
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(hostname)
buf, err := json.Marshal(authConfig)
if err != nil {
return err
}
2013-08-30 23:15:07 +02:00
registryAuthHeader := []string{
base64.URLEncoding.EncodeToString(buf),
}
2013-11-22 12:14:34 -08:00
if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
2013-05-02 05:07:06 +02:00
return err
}
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil {
return err
}
2013-11-25 23:20:36 -08:00
} else if err != nil {
return err
}
var runResult engine.Env
if err := runResult.Decode(stream); err != nil {
2013-05-02 05:07:06 +02:00
return err
}
2013-05-02 05:07:06 +02:00
for _, warning := range runResult.GetList("Warnings") {
2013-06-27 15:25:31 -07:00
fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
2013-05-02 18:36:23 +02:00
}
2013-11-22 12:14:34 -08:00
if len(hostConfig.ContainerIDFile) > 0 {
if _, err = containerIDFile.Write([]byte(runResult.Get("Id"))); err != nil {
return fmt.Errorf("Failed to write the container ID to the file: %s", err)
}
}
2013-05-02 18:36:23 +02:00
if sigProxy {
sigc := cli.forwardAllSignals(runResult.Get("Id"))
defer signal.StopCatch(sigc)
}
var (
2013-11-22 12:14:34 -08:00
waitDisplayId chan struct{}
errCh chan error
)
if !config.AttachStdout && !config.AttachStderr {
// Make this asynchrone in order to let the client write to stdin before having to read the ID
2013-11-22 12:14:34 -08:00
waitDisplayId = make(chan struct{})
go func() {
2013-11-22 12:14:34 -08:00
defer close(waitDisplayId)
fmt.Fprintf(cli.out, "%s\n", runResult.Get("Id"))
}()
2013-06-24 12:12:06 -07:00
}
// We need to instanciate the chan because the select needs it. It can
// be closed but can't be uninitialized.
hijacked := make(chan io.Closer)
// Block the return until the chan gets closed
defer func() {
utils.Debugf("End of CmdRun(), Waiting for hijack to finish.")
if _, ok := <-hijacked; ok {
utils.Errorf("Hijack did not finish (chan still open)")
}
}()
2013-05-29 11:40:54 +00:00
2013-06-24 12:12:06 -07:00
if config.AttachStdin || config.AttachStdout || config.AttachStderr {
2013-11-22 12:14:34 -08:00
var (
out, stderr io.Writer
in io.ReadCloser
v = url.Values{}
)
2013-05-29 11:40:54 +00:00
v.Set("stream", "1")
if config.AttachStdin {
v.Set("stdin", "1")
2013-10-25 17:17:33 -07:00
in = cli.in
2013-05-29 11:40:54 +00:00
}
if config.AttachStdout {
v.Set("stdout", "1")
out = cli.out
2013-05-29 11:40:54 +00:00
}
2013-06-17 15:45:08 -07:00
if config.AttachStderr {
2013-05-29 11:40:54 +00:00
v.Set("stderr", "1")
if config.Tty {
stderr = cli.out
} else {
stderr = cli.err
}
2013-05-29 11:40:54 +00:00
}
errCh = utils.Go(func() error {
return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked)
})
} else {
close(hijacked)
}
// Acknowledge the hijack before starting
select {
case closer := <-hijacked:
// Make sure that hijack gets closed when returning. (result
// in closing hijack chan and freeing server's goroutines.
if closer != nil {
defer closer.Close()
}
case err := <-errCh:
if err != nil {
utils.Debugf("Error hijack: %s", err)
return err
}
}
//start the container
if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/start", hostConfig, false)); err != nil {
return err
}
2013-07-20 13:47:13 +03:00
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal {
if err := cli.monitorTtySize(runResult.Get("Id")); err != nil {
utils.Errorf("Error monitoring TTY size: %s\n", err)
}
}
if errCh != nil {
if err := <-errCh; err != nil {
2013-06-17 15:45:08 -07:00
utils.Debugf("Error hijack: %s", err)
return err
2013-05-02 05:07:06 +02:00
}
2013-05-07 21:36:24 +02:00
}
2013-11-22 12:14:34 -08:00
// Detached mode: wait for the id to be displayed and return.
if !config.AttachStdout && !config.AttachStderr {
// Detached mode
2013-11-22 12:14:34 -08:00
<-waitDisplayId
return nil
}
2013-11-22 11:23:48 -08:00
2013-11-22 12:14:34 -08:00
var status int
// Attached mode
if autoRemove {
// Autoremove: wait for the container to finish, retrieve
// the exit code and remove the container
if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
return err
}
if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
2013-11-22 12:14:34 -08:00
return err
2013-09-26 14:52:37 -07:00
}
if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.Get("Id")+"?v=1", nil, false)); err != nil {
2013-11-22 12:14:34 -08:00
return err
}
} else {
if !config.Tty {
// In non-tty mode, we can't dettach, so we know we need to wait.
if status, err = waitForExit(cli, runResult.Get("Id")); err != nil {
return err
}
} else {
// In TTY mode, there is a race. If the process dies too slowly, the state can be update after the getExitCode call
// and result in a wrong exit code.
// No Autoremove: Simply retrieve the exit code
if _, status, err = getExitCode(cli, runResult.Get("Id")); err != nil {
return err
}
2013-09-09 21:26:35 +00:00
}
}
2013-11-22 12:14:34 -08:00
if status != 0 {
return &utils.StatusError{StatusCode: status}
2013-11-22 12:14:34 -08:00
}
2013-05-02 05:07:06 +02:00
return nil
}
func (cli *DockerCli) CmdCp(args ...string) error {
cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 2 {
cmd.Usage()
return nil
}
var copyData engine.Env
info := strings.Split(cmd.Arg(0), ":")
if len(info) != 2 {
return fmt.Errorf("Error: Path not specified")
}
copyData.Set("Resource", info[1])
copyData.Set("HostPath", cmd.Arg(1))
stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
if stream != nil {
defer stream.Close()
}
if statusCode == 404 {
return fmt.Errorf("No such container: %v", info[0])
}
if err != nil {
return err
}
if statusCode == 200 {
if err := archive.Untar(stream, copyData.Get("HostPath"), &archive.TarOptions{NoLchown: true}); err != nil {
return err
}
}
return nil
}
2013-09-02 09:06:17 -07:00
func (cli *DockerCli) CmdSave(args ...string) error {
cmd := cli.Subcmd("save", "IMAGE", "Save an image to a tar archive (streamed to STDOUT by default)")
outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
2013-09-02 09:06:17 -07:00
if err := cmd.Parse(args); err != nil {
return err
2013-09-02 09:06:17 -07:00
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
var (
output io.Writer = cli.out
err error
)
if *outfile != "" {
output, err = os.Create(*outfile)
if err != nil {
return err
}
}
2013-09-02 09:06:17 -07:00
image := cmd.Arg(0)
if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil {
2013-09-02 09:06:17 -07:00
return err
}
return nil
}
func (cli *DockerCli) CmdLoad(args ...string) error {
cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN")
infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
if err := cmd.Parse(args); err != nil {
return err
}
2013-09-02 09:06:17 -07:00
if cmd.NArg() != 0 {
cmd.Usage()
return nil
}
var (
input io.Reader = cli.in
err error
)
if *infile != "" {
input, err = os.Open(*infile)
if err != nil {
return err
}
}
if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil {
return err
2013-09-02 09:06:17 -07:00
}
return nil
}