diff --git a/.drone.yml b/.drone.yml new file mode 100755 index 0000000000..53e00e0fda --- /dev/null +++ b/.drone.yml @@ -0,0 +1,14 @@ +image: dockercore/docker +env: + - AUTO_GOPATH=1 + - DOCKER_GRAPHDRIVER=vfs + - DOCKER_EXECDRIVER=native +script: +# Setup the DockerInDocker environment. + - hack/dind +# Tests relying on StartWithBusybox make Drone time out. + - rm integration-cli/docker_cli_daemon_test.go + - rm integration-cli/docker_cli_exec_test.go +# Validate and test. + - hack/make.sh validate-dco validate-gofmt + - hack/make.sh binary cross test-unit test-integration-cli test-integration diff --git a/.travis.yml b/.travis.yml index 55fa9044c9..174afae88a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,15 @@ sudo: false install: - export DOCKER_BUILDTAGS='exclude_graphdriver_btrfs exclude_graphdriver_devicemapper' # btrfs and devicemapper fail to compile thanks to a couple missing headers (which we can't install thanks to "sudo: false") - export AUTO_GOPATH=1 +# some of Docker's unit tests don't work inside Travis (yet!), so we purge those test files for now + - rm -f daemon/graphdriver/btrfs/*_test.go # fails to compile (missing header) + - rm -f daemon/graphdriver/devmapper/*_test.go # fails to compile (missing header) + - rm -f daemon/execdriver/lxc/*_test.go # fails to run (missing "lxc-start") + - rm -f daemon/graphdriver/aufs/*_test.go # fails to run ("backing file system is unsupported for this graph driver") + - rm -f daemon/graphdriver/vfs/*_test.go # fails to run (not root, which these tests assume "/var/tmp/... no owned by uid 0") + - rm -f daemon/networkdriver/bridge/*_test.go # fails to run ("Failed to initialize network driver") + - rm -f graph/*_test.go # fails to run ("mkdir /tmp/docker-test.../vfs/dir/foo/etc/postgres: permission denied") + - rm -f pkg/mount/*_test.go # fails to run ("permission denied") before_script: - env | sort @@ -24,7 +33,7 @@ before_script: script: - hack/make.sh validate-dco - hack/make.sh validate-gofmt - - ./hack/make.sh dynbinary - DOCKER_CLIENTONLY=1 ./hack/make.sh dynbinary + - ./hack/make.sh dynbinary dyntest-unit # vim:set sw=2 ts=2: diff --git a/Dockerfile b/Dockerfile index 8f47b0de75..34b0460c07 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,7 +59,7 @@ RUN cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper # see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL # Install Go -RUN curl -sSL https://golang.org/dl/go1.3.1.src.tar.gz | tar -v -C /usr/local -xz +RUN curl -sSL https://golang.org/dl/go1.3.3.src.tar.gz | tar -v -C /usr/local -xz ENV PATH /usr/local/go/bin:$PATH ENV GOPATH /go:/go/src/github.com/docker/docker/vendor ENV PATH /go/bin:$PATH @@ -89,8 +89,11 @@ RUN mkdir -p /go/src/github.com/cpuguy83 \ # Get the "busybox" image source so we can build locally instead of pulling RUN git clone -b buildroot-2014.02 https://github.com/jpetazzo/docker-busybox.git /docker-busybox +# Get the "cirros" image source so we can import it instead of fetching it during tests +RUN curl -sSL -o /cirros.tar.gz https://github.com/ewindisch/docker-cirros/raw/1cded459668e8b9dbf4ef976c94c05add9bbd8e9/cirros-0.3.0-x86_64-lxc.tar.gz + # Setup s3cmd config -RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg +RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > $HOME/.s3cfg # Set user.email so crosbymichael's in-container merge commits go smoothly RUN git config --global user.email 'docker-dummy@example.com' diff --git a/Makefile b/Makefile index 40c623a47a..842cc18e71 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH)) DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH)) DOCKER_MOUNT := $(if $(BINDDIR),-v "$(CURDIR)/$(BINDDIR):/go/src/github.com/docker/docker/$(BINDDIR)") -DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TESTFLAGS -e TESTDIRS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)" +DOCKER_RUN_DOCKER := docker run --rm -it --privileged -e TIMEOUT -e BUILDFLAGS -e TESTFLAGS -e TESTDIRS -e DOCKER_GRAPHDRIVER -e DOCKER_EXECDRIVER $(DOCKER_MOUNT) "$(DOCKER_IMAGE)" # to allow `make DOCSDIR=docs docs-shell` DOCKER_RUN_DOCS := docker run --rm -it $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) -e AWS_S3_BUCKET diff --git a/VERSION b/VERSION index 26aaba0e86..0c64b5c3d1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.0 +1.2.0-dev diff --git a/api/client/cli.go b/api/client/cli.go index d80f9cc32c..6bc3fc3507 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -13,6 +13,7 @@ import ( flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/term" "github.com/docker/docker/registry" + "github.com/docker/libtrust" ) type DockerCli struct { @@ -22,10 +23,17 @@ type DockerCli struct { in io.ReadCloser out io.Writer err io.Writer - isTerminal bool - terminalFd uintptr + key libtrust.PrivateKey tlsConfig *tls.Config scheme string + // inFd holds file descriptor of the client's STDIN, if it's a valid file + inFd uintptr + // outFd holds file descriptor of the client's STDOUT, if it's a valid file + outFd uintptr + // isTerminalIn describes if client's STDIN is a TTY + isTerminalIn bool + // isTerminalOut describes if client's STDOUT is a TTY + isTerminalOut bool } var funcMap = template.FuncMap{ @@ -35,11 +43,15 @@ var funcMap = template.FuncMap{ }, } -func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) { - if len(name) == 0 { - return nil, false +func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) { + camelArgs := make([]string, len(args)) + for i, s := range args { + if len(s) == 0 { + return nil, false + } + camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) } - methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) + methodName := "Cmd" + strings.Join(camelArgs, "") method := reflect.ValueOf(cli).MethodByName(methodName) if !method.IsValid() { return nil, false @@ -49,6 +61,12 @@ func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) { // Cmd executes the specified command func (cli *DockerCli) Cmd(args ...string) error { + if len(args) > 1 { + method, exists := cli.getMethod(args[:2]...) + if exists { + return method(args[2:]...) + } + } if len(args) > 0 { method, exists := cli.getMethod(args[0]) if !exists { @@ -63,7 +81,11 @@ func (cli *DockerCli) Cmd(args ...string) error { func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet { flags := flag.NewFlagSet(name, flag.ContinueOnError) flags.Usage = func() { - fmt.Fprintf(cli.err, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description) + options := "" + if flags.FlagCountUndeprecated() > 0 { + options = "[OPTIONS] " + } + fmt.Fprintf(cli.err, "\nUsage: docker %s %s%s\n\n%s\n\n", name, options, signature, description) flags.PrintDefaults() os.Exit(2) } @@ -78,11 +100,13 @@ func (cli *DockerCli) LoadConfigFile() (err error) { return err } -func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli { +func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey, proto, addr string, tlsConfig *tls.Config) *DockerCli { var ( - isTerminal = false - terminalFd uintptr - scheme = "http" + inFd uintptr + outFd uintptr + isTerminalIn = false + isTerminalOut = false + scheme = "http" ) if tlsConfig != nil { @@ -90,24 +114,35 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC } if in != nil { + if file, ok := in.(*os.File); ok { + inFd = file.Fd() + isTerminalIn = term.IsTerminal(inFd) + } + } + + if out != nil { if file, ok := out.(*os.File); ok { - terminalFd = file.Fd() - isTerminal = term.IsTerminal(terminalFd) + outFd = file.Fd() + isTerminalOut = term.IsTerminal(outFd) } } if err == nil { err = out } + return &DockerCli{ - proto: proto, - addr: addr, - in: in, - out: out, - err: err, - isTerminal: isTerminal, - terminalFd: terminalFd, - tlsConfig: tlsConfig, - scheme: scheme, + proto: proto, + addr: addr, + in: in, + out: out, + err: err, + key: key, + inFd: inFd, + outFd: outFd, + isTerminalIn: isTerminalIn, + isTerminalOut: isTerminalOut, + tlsConfig: tlsConfig, + scheme: scheme, } } diff --git a/api/client/commands.go b/api/client/commands.go index 81b0668cda..6c4e5c55fe 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -23,16 +23,20 @@ import ( "time" "github.com/docker/docker/api" - "github.com/docker/docker/archive" "github.com/docker/docker/dockerversion" "github.com/docker/docker/engine" + "github.com/docker/docker/graph" "github.com/docker/docker/nat" "github.com/docker/docker/opts" + "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/log" + flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/filters" + "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/term" + "github.com/docker/docker/pkg/timeutils" "github.com/docker/docker/pkg/units" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" @@ -44,6 +48,13 @@ const ( ) func (cli *DockerCli) CmdHelp(args ...string) error { + if len(args) > 1 { + method, exists := cli.getMethod(args[:2]...) + if exists { + method("--help") + return nil + } + } if len(args) > 0 { method, exists := cli.getMethod(args[0]) if !exists { @@ -53,52 +64,14 @@ func (cli *DockerCli) CmdHelp(args ...string) error { 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{ - {"attach", "Attach to a running container"}, - {"build", "Build an image from a Dockerfile"}, - {"commit", "Create a new image from a container's changes"}, - {"cp", "Copy files/folders from a container's filesystem to the host path"}, - {"diff", "Inspect changes on a container's filesystem"}, - {"events", "Get real time events from the server"}, - {"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"}, - {"load", "Load an image from a tar archive"}, - {"login", "Register or log in to a Docker registry server"}, - {"logout", "Log out from a Docker registry server"}, - {"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"}, - {"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"}, - {"restart", "Restart a running container"}, - {"rm", "Remove one or more containers"}, - {"rmi", "Remove one or more images"}, - {"run", "Run a command in a new container"}, - {"save", "Save an image to a tar archive"}, - {"search", "Search for an image on the Docker Hub"}, - {"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"}, - } { - help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1]) - } - fmt.Fprintf(cli.err, "%s\n", help) + + flag.Usage() + 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") + cmd := cli.Subcmd("build", "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") @@ -203,10 +176,15 @@ func (cli *DockerCli) CmdBuild(args ...string) error { //Check if the given image name can be resolved if *tag != "" { - repository, _ := parsers.ParseRepositoryTag(*tag) + repository, tag := parsers.ParseRepositoryTag(*tag) if _, _, err := registry.ResolveRepositoryName(repository); err != nil { return err } + if len(tag) > 0 { + if err := graph.ValidateTagName(tag); err != nil { + return err + } + } } v.Set("t", *tag) @@ -255,7 +233,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // '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.") + cmd := cli.Subcmd("login", "[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 @@ -302,16 +280,18 @@ func (cli *DockerCli) CmdLogin(args ...string) error { username = authconfig.Username } } + // Assume that a different username means they may not want to use + // the password or email from the config file, so prompt them if username != authconfig.Username { if password == "" { - oldState, _ := term.SaveState(cli.terminalFd) + oldState, _ := term.SaveState(cli.inFd) fmt.Fprintf(cli.out, "Password: ") - term.DisableEcho(cli.terminalFd, oldState) + term.DisableEcho(cli.inFd, oldState) password = readInput(cli.in, cli.out) fmt.Fprint(cli.out, "\n") - term.RestoreTerminal(cli.terminalFd, oldState) + term.RestoreTerminal(cli.inFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } @@ -325,8 +305,16 @@ func (cli *DockerCli) CmdLogin(args ...string) error { } } } else { - password = authconfig.Password - email = authconfig.Email + // However, if they don't override the username use the + // password or email from the cmd line if specified. IOW, allow + // then to change/overide them. And if not specified, just + // use what's in the config file + if password == "" { + password = authconfig.Password + } + if email == "" { + email = authconfig.Email + } } authconfig.Username = username authconfig.Password = password @@ -529,7 +517,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { } 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") + cmd := cli.Subcmd("stop", "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 @@ -556,7 +544,7 @@ func (cli *DockerCli) CmdStop(args ...string) error { } func (cli *DockerCli) CmdRestart(args ...string) error { - cmd := cli.Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container") + cmd := cli.Subcmd("restart", "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 @@ -661,8 +649,8 @@ func (cli *DockerCli) CmdStart(args ...string) error { 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) + cErr = promise.Go(func() error { + return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil) }) } @@ -683,14 +671,13 @@ func (cli *DockerCli) CmdStart(args ...string) error { 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 { + if tty && cli.isTerminalOut { + if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { log.Errorf("Error monitoring TTY size: %s", err) } } @@ -861,26 +848,15 @@ func (cli *DockerCli) CmdTop(args ...string) error { } 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") + cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT") if err := cmd.Parse(args); err != nil { return nil } - if cmd.NArg() != 2 { + if cmd.NArg() < 1 { cmd.Usage() return nil } - var ( - port = cmd.Arg(1) - proto = "tcp" - parts = strings.SplitN(port, "/", 2) - ) - - if len(parts) == 2 && len(parts[1]) != 0 { - port = parts[0] - proto = parts[1] - } - steam, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) if err != nil { return err @@ -895,14 +871,34 @@ func (cli *DockerCli) CmdPort(args ...string) error { return err } - 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) + if cmd.NArg() == 2 { + var ( + port = cmd.Arg(1) + proto = "tcp" + parts = strings.SplitN(port, "/", 2) + ) + + if len(parts) == 2 && len(parts[1]) != 0 { + port = parts[0] + proto = parts[1] } - return nil + natPort := port + "/" + proto + 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) + } + return nil + } + return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0)) } - return fmt.Errorf("Error: No public port '%s' published for %s", cmd.Arg(1), cmd.Arg(0)) + for from, frontends := range ports { + for _, frontend := range frontends { + fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort) + } + } + + return nil } // 'docker rmi IMAGE' removes all images with the name IMAGE @@ -954,7 +950,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error { } func (cli *DockerCli) CmdHistory(args ...string) error { - cmd := cli.Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image") + cmd := cli.Subcmd("history", "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") @@ -1011,7 +1007,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error { } func (cli *DockerCli) CmdRm(args ...string) error { - cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers") + cmd := cli.Subcmd("rm", "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 the removal of a running container (uses SIGKILL)") @@ -1051,7 +1047,7 @@ func (cli *DockerCli) CmdRm(args ...string) error { // '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") + cmd := cli.Subcmd("kill", "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 { @@ -1101,7 +1097,8 @@ func (cli *DockerCli) CmdImport(args ...string) error { if repository != "" { //Check if the given image name can be resolved - if _, _, err := registry.ResolveRepositoryName(repository); err != nil { + repo, _ := parsers.ParseRepositoryTag(repository) + if _, _, err := registry.ResolveRepositoryName(repo); err != nil { return err } } @@ -1182,7 +1179,7 @@ func (cli *DockerCli) CmdPush(args ...string) error { 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") + allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") if err := cmd.Parse(args); err != nil { return nil } @@ -1192,19 +1189,22 @@ func (cli *DockerCli) CmdPull(args ...string) error { return nil } var ( - v = url.Values{} - remote = cmd.Arg(0) + v = url.Values{} + remote = cmd.Arg(0) + newRemote = remote ) - - v.Set("fromImage", remote) - - if *tag == "" { - v.Set("tag", *tag) + taglessRemote, tag := parsers.ParseRepositoryTag(remote) + if tag == "" && !*allTags { + newRemote = taglessRemote + ":latest" + } + if tag != "" && *allTags { + return fmt.Errorf("tag can't be used with --all-tags/-a") } - remote, _ = parsers.ParseRepositoryTag(remote) + v.Set("fromImage", newRemote) + // Resolve the Repository name from fqn to hostname + name - hostname, _, err := registry.ResolveRepositoryName(remote) + hostname, _, err := registry.ResolveRepositoryName(taglessRemote) if err != nil { return err } @@ -1244,7 +1244,7 @@ func (cli *DockerCli) CmdPull(args ...string) error { } func (cli *DockerCli) CmdImages(args ...string) error { - cmd := cli.Subcmd("images", "[OPTIONS] [NAME]", "List images") + cmd := cli.Subcmd("images", "[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") @@ -1475,57 +1475,68 @@ func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix stri } 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.") + var ( + err error - flFilter := opts.NewListOpts(nil) - cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited= - containers with exit code of ") + psFilterArgs = filters.Args{} + v = url.Values{} + + cmd = cli.Subcmd("ps", "", "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.") + flFilter = opts.NewListOpts(nil) + ) + + cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited= - containers with exit code of \nstatus=(restarting|running|paused|exited)") if err := cmd.Parse(args); err != nil { return nil } - v := url.Values{} + if *last == -1 && *nLatest { *last = 1 } + if *all { v.Set("all", "1") } + if *last != -1 { v.Set("limit", strconv.Itoa(*last)) } + if *since != "" { v.Set("since", *since) } + if *before != "" { v.Set("before", *before) } + if *size { v.Set("size", "1") } // Consolidate all filter flags, and sanity check them. // They'll get processed in the daemon/server. - psFilterArgs := filters.Args{} for _, f := range flFilter.GetAll() { - var err error - psFilterArgs, err = filters.ParseFlag(f, psFilterArgs) - if err != nil { + if psFilterArgs, err = filters.ParseFlag(f, psFilterArgs); err != nil { return err } } + if len(psFilterArgs) > 0 { filterJson, err := filters.ToParam(psFilterArgs) if err != nil { return err } + v.Set("filters", filterJson) } @@ -1538,9 +1549,11 @@ func (cli *DockerCli) CmdPs(args ...string) error { if _, err := outs.ReadListFrom(body); err != nil { return err } + w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") + if *size { fmt.Fprintln(w, "\tSIZE") } else { @@ -1548,54 +1561,74 @@ func (cli *DockerCli) CmdPs(args ...string) error { } } + stripNamePrefix := func(ss []string) []string { + for i, s := range ss { + ss[i] = s[1:] + } + + return ss + } + for _, out := range outs.Data { - var ( - outID = out.Get("Id") - outNames = out.GetList("Names") - ) + outID := out.Get("Id") 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 { + fmt.Fprintln(w, outID) + + continue } - if !*quiet { - var ( - outCommand = out.Get("Command") - ports = engine.NewTable("", 0) - ) - outCommand = strconv.Quote(outCommand) - if !*noTrunc { - outCommand = utils.Trunc(outCommand, 20) - } - 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, ",")) - if *size { - if out.GetInt("SizeRootFs") > 0 { - fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(out.GetInt64("SizeRw")), units.HumanSize(out.GetInt64("SizeRootFs"))) - } else { - fmt.Fprintf(w, "%s\n", units.HumanSize(out.GetInt64("SizeRw"))) + var ( + outNames = stripNamePrefix(out.GetList("Names")) + outCommand = strconv.Quote(out.Get("Command")) + ports = engine.NewTable("", 0) + ) + + if !*noTrunc { + outCommand = utils.Trunc(outCommand, 20) + + // only display the default name for the container with notrunc is passed + for _, name := range outNames { + if len(strings.Split(name, "/")) == 1 { + outNames = []string{name} + + break } - } else { - fmt.Fprint(w, "\n") } - } else { - fmt.Fprintln(w, outID) } + + 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, ",")) + + if *size { + if out.GetInt("SizeRootFs") > 0 { + fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(out.GetInt64("SizeRw")), units.HumanSize(out.GetInt64("SizeRootFs"))) + } else { + fmt.Fprintf(w, "%s\n", units.HumanSize(out.GetInt64("SizeRw"))) + } + + continue + } + + fmt.Fprint(w, "\n") } 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") + cmd := cli.Subcmd("commit", "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 (e.g., \"John Hannibal Smith \")") @@ -1656,7 +1689,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error { } func (cli *DockerCli) CmdEvents(args ...string) error { - cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server") + cmd := cli.Subcmd("events", "", "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") if err := cmd.Parse(args); err != nil { @@ -1672,7 +1705,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error { loc = time.FixedZone(time.Now().Zone()) ) var setTime = func(key, value string) { - format := time.RFC3339Nano + format := timeutils.RFC3339NanoFixed if len(value) < len(format) { format = format[:len(value)] } @@ -1792,7 +1825,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error { func (cli *DockerCli) CmdAttach(args ...string) error { var ( - cmd = cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container") + cmd = cli.Subcmd("attach", "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, "Proxy all received signals to the process (even in non-TTY mode). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.") ) @@ -1826,8 +1859,8 @@ func (cli *DockerCli) CmdAttach(args ...string) error { tty = config.GetBool("Tty") ) - if tty && cli.isTerminal { - if err := cli.monitorTtySize(cmd.Arg(0)); err != nil { + if tty && cli.isTerminalOut { + if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { log.Debugf("Error monitoring TTY size: %s", err) } } @@ -1849,7 +1882,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error { 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 { + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, nil, nil); err != nil { return err } @@ -1920,7 +1953,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error { type ports []int func (cli *DockerCli) CmdTag(args ...string) error { - cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository") + cmd := cli.Subcmd("tag", "IMAGE[:TAG] [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 @@ -1981,15 +2014,112 @@ func (cli *DockerCli) pullImage(image string) error { registryAuthHeader := []string{ base64.URLEncoding.EncodeToString(buf), } - if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { + if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); 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) +type cidFile struct { + path string + file *os.File + written bool +} + +func newCIDFile(path string) (*cidFile, error) { + if _, err := os.Stat(path); err == nil { + return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path) + } + f, err := os.Create(path) + if err != nil { + return nil, fmt.Errorf("Failed to create the container ID file: %s", err) + } + + return &cidFile{path: path, file: f}, nil +} + +func (cid *cidFile) Close() error { + cid.file.Close() + + if !cid.written { + if err := os.Remove(cid.path); err != nil { + return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err) + } + } + + return nil +} + +func (cid *cidFile) Write(id string) error { + if _, err := cid.file.Write([]byte(id)); err != nil { + return fmt.Errorf("Failed to write the container ID to the file: %s", err) + } + cid.written = true + return nil +} + +func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) { + containerValues := url.Values{} + if name != "" { + containerValues.Set("name", name) + } + + mergedConfig := runconfig.MergeConfigs(config, hostConfig) + + var containerIDFile *cidFile + if cidfile != "" { + var err error + if containerIDFile, err = newCIDFile(cidfile); err != nil { + return nil, err + } + defer containerIDFile.Close() + } + + //create the container + stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false) + //if image not found try to pull it + if statusCode == 404 { + fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image) + + if err = cli.pullImage(config.Image); err != nil { + return nil, err + } + // Retry + if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil { + return nil, err + } + } else if err != nil { + return nil, err + } + + var result engine.Env + if err := result.Decode(stream); err != nil { + return nil, err + } + + for _, warning := range result.GetList("Warnings") { + fmt.Fprintf(cli.err, "WARNING: %s\n", warning) + } + + if containerIDFile != nil { + if err = containerIDFile.Write(result.Get("Id")); err != nil { + return nil, err + } + } + + return result, nil + +} + +func (cli *DockerCli) CmdCreate(args ...string) error { + cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container") + + // These are flags not stored in Config/HostConfig + var ( + flName = cmd.String([]string{"-name"}, "", "Assign a name to the container") + ) + + config, hostConfig, cmd, err := runconfig.Parse(cmd, args, nil) if err != nil { return err } @@ -1998,82 +2128,70 @@ func (cli *DockerCli) CmdRun(args ...string) error { return nil } - // Retrieve relevant client-side config + createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) + if err != nil { + return err + } + + fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id")) + + return nil +} + +func (cli *DockerCli) CmdRun(args ...string) error { + // FIXME: just use runconfig.Parse already + cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container") + + // These are flags not stored in Config/HostConfig 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()) + flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") + flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run the container in the background and print the new container ID") + flSigProxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process (even in non-TTY mode). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.") + flName = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container") + flAttach *opts.ListOpts + + ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d") + ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm") + ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d") ) - // Disable sigProxy in case on TTY + config, hostConfig, cmd, err := runconfig.Parse(cmd, args, nil) + if err != nil { + return err + } + if config.Image == "" { + cmd.Usage() + return nil + } + + if *flDetach { + if fl := cmd.Lookup("attach"); fl != nil { + flAttach = fl.Value.(*opts.ListOpts) + if flAttach.Len() != 0 { + return ErrConflictAttachDetach + } + } + if *flAutoRemove { + return ErrConflictDetachAutoRemove + } + + config.AttachStdin = false + config.AttachStdout = false + config.AttachStderr = false + config.StdinOnce = false + } + + // Disable flSigProxy in case on TTY + sigProxy := *flSigProxy if config.Tty { sigProxy = false } - var containerIDFile io.WriteCloser - if len(hostConfig.ContainerIDFile) > 0 { - 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) - } - 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) - } - } - }() - } - - containerValues := url.Values{} - if name := flName.Value.String(); name != "" { - containerValues.Set("name", name) - } - - //create the container - stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false) - //if image not found try to pull it - if statusCode == 404 { - fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image) - - if err = cli.pullImage(config.Image); err != nil { - return err - } - // Retry - if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil { - return err - } - } else if err != nil { + runResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) + if err != nil { return err } - var runResult engine.Env - if err := runResult.Decode(stream); err != nil { - return err - } - - for _, warning := range runResult.GetList("Warnings") { - fmt.Fprintf(cli.err, "WARNING: %s\n", warning) - } - - 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) - } - } - if sigProxy { sigc := cli.forwardAllSignals(runResult.Get("Id")) defer signal.StopCatch(sigc) @@ -2093,6 +2211,10 @@ func (cli *DockerCli) CmdRun(args ...string) error { }() } + if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") { + return ErrConflictRestartPolicyAndAutoRemove + } + // 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) @@ -2130,8 +2252,8 @@ func (cli *DockerCli) CmdRun(args ...string) error { } } - errCh = utils.Go(func() error { - return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked) + errCh = promise.Go(func() error { + return cli.hijack("POST", "/containers/"+runResult.Get("Id")+"/attach?"+v.Encode(), config.Tty, in, out, stderr, hijacked, nil) }) } else { close(hijacked) @@ -2157,8 +2279,8 @@ func (cli *DockerCli) CmdRun(args ...string) error { return err } - if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminal { - if err := cli.monitorTtySize(runResult.Get("Id")); err != nil { + if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { + if err := cli.monitorTtySize(runResult.Get("Id"), false); err != nil { log.Errorf("Error monitoring TTY size: %s", err) } } @@ -2180,7 +2302,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { var status int // Attached mode - if autoRemove { + if *flAutoRemove { // 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 { @@ -2254,14 +2376,14 @@ func (cli *DockerCli) CmdCp(args ...string) error { } 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") + cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)") + outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT") if err := cmd.Parse(args); err != nil { return err } - if cmd.NArg() != 1 { + if cmd.NArg() < 1 { cmd.Usage() return nil } @@ -2276,9 +2398,19 @@ func (cli *DockerCli) CmdSave(args ...string) error { return err } } - image := cmd.Arg(0) - if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil { - return err + if len(cmd.Args()) == 1 { + image := cmd.Arg(0) + if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil { + return err + } + } else { + v := url.Values{} + for _, arg := range cmd.Args() { + v.Add("names", arg) + } + if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil { + return err + } } return nil } @@ -2311,3 +2443,101 @@ func (cli *DockerCli) CmdLoad(args ...string) error { } return nil } + +func (cli *DockerCli) CmdExec(args ...string) error { + cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in an existing container") + + execConfig, err := runconfig.ParseExec(cmd, args) + if err != nil { + return err + } + if execConfig.Container == "" { + cmd.Usage() + return nil + } + + stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false) + if err != nil { + return err + } + + var execResult engine.Env + if err := execResult.Decode(stream); err != nil { + return err + } + + execID := execResult.Get("Id") + + if execID == "" { + fmt.Fprintf(cli.out, "exec ID empty") + return nil + } + + if execConfig.Detach { + if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil { + return err + } + return nil + } + + // Interactive exec requested. + var ( + out, stderr io.Writer + in io.ReadCloser + hijacked = make(chan io.Closer) + errCh chan error + ) + + // Block the return until the chan gets closed + defer func() { + log.Debugf("End of CmdExec(), Waiting for hijack to finish.") + if _, ok := <-hijacked; ok { + log.Errorf("Hijack did not finish (chan still open)") + } + }() + + if execConfig.AttachStdin { + in = cli.in + } + if execConfig.AttachStdout { + out = cli.out + } + if execConfig.AttachStderr { + if execConfig.Tty { + stderr = cli.out + } else { + stderr = cli.err + } + } + errCh = promise.Go(func() error { + return cli.hijack("POST", "/exec/"+execID+"/start", execConfig.Tty, in, out, stderr, hijacked, execConfig) + }) + + // 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 { + log.Debugf("Error hijack: %s", err) + return err + } + } + + if execConfig.Tty && cli.isTerminalIn { + if err := cli.monitorTtySize(execID, true); err != nil { + log.Errorf("Error monitoring TTY size: %s", err) + } + } + + if err := <-errCh; err != nil { + log.Debugf("Error hijack: %s", err) + return err + } + + return nil +} diff --git a/api/client/hijack.go b/api/client/hijack.go index ba6ebfb0d8..00170a4a37 100644 --- a/api/client/hijack.go +++ b/api/client/hijack.go @@ -14,8 +14,9 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/dockerversion" "github.com/docker/docker/pkg/log" + "github.com/docker/docker/pkg/promise" + "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/term" - "github.com/docker/docker/utils" ) func (cli *DockerCli) dial() (net.Conn, error) { @@ -25,14 +26,18 @@ func (cli *DockerCli) dial() (net.Conn, error) { return net.Dial(cli.proto, cli.addr) } -func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer) error { +func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}) error { defer func() { if started != nil { close(started) } }() - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), nil) + params, err := cli.encodeData(data) + if err != nil { + return err + } + req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params) if err != nil { return err } @@ -64,20 +69,20 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea var oldState *term.State - if in != nil && setRawTerminal && cli.isTerminal && os.Getenv("NORAW") == "" { - oldState, err = term.SetRawTerminal(cli.terminalFd) + if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" { + oldState, err = term.SetRawTerminal(cli.inFd) if err != nil { return err } - defer term.RestoreTerminal(cli.terminalFd, oldState) + defer term.RestoreTerminal(cli.inFd, oldState) } if stdout != nil || stderr != nil { - receiveStdout = utils.Go(func() (err error) { + receiveStdout = promise.Go(func() (err error) { defer func() { if in != nil { - if setRawTerminal && cli.isTerminal { - term.RestoreTerminal(cli.terminalFd, oldState) + if setRawTerminal && cli.isTerminalIn { + term.RestoreTerminal(cli.inFd, oldState) } // For some reason this Close call blocks on darwin.. // As the client exists right after, simply discard the close @@ -92,14 +97,14 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea if setRawTerminal && stdout != nil { _, err = io.Copy(stdout, br) } else { - _, err = utils.StdCopy(stdout, stderr, br) + _, err = stdcopy.StdCopy(stdout, stderr, br) } log.Debugf("[hijack] End of stdout") return err }) } - sendStdin := utils.Go(func() error { + sendStdin := promise.Go(func() error { if in != nil { io.Copy(rwc, in) log.Debugf("[hijack] End of stdin") @@ -124,7 +129,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea } } - if !cli.isTerminal { + if !cli.isTerminalIn { if err := <-sendStdin; err != nil { log.Debugf("Error sendStdin: %s", err) return err diff --git a/api/client/utils.go b/api/client/utils.go index e4ef8d3875..58b730bd1b 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -16,11 +16,13 @@ import ( "strconv" "strings" "syscall" + "time" "github.com/docker/docker/api" "github.com/docker/docker/dockerversion" "github.com/docker/docker/engine" "github.com/docker/docker/pkg/log" + "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/term" "github.com/docker/docker/registry" "github.com/docker/docker/utils" @@ -34,30 +36,46 @@ func (cli *DockerCli) HTTPClient() *http.Client { tr := &http.Transport{ TLSClientConfig: cli.tlsConfig, Dial: func(network, addr string) (net.Conn, error) { - return net.Dial(cli.proto, cli.addr) + // Why 32? See issue 8035 + return net.DialTimeout(cli.proto, cli.addr, 32*time.Second) }, } + if cli.proto == "unix" { + // XXX workaround for net/http Transport which caches connections, but is + // intended for tcp connections, not unix sockets. + tr.DisableKeepAlives = true + + // no need in compressing for local communications + tr.DisableCompression = true + } return &http.Client{Transport: tr} } -func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) { +func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) { params := bytes.NewBuffer(nil) if data != nil { if env, ok := data.(engine.Env); ok { if err := env.Encode(params); err != nil { - return nil, -1, err + return nil, err } } else { buf, err := json.Marshal(data) if err != nil { - return nil, -1, err + return nil, err } if _, err := params.Write(buf); err != nil { - return nil, -1, err + return nil, err } } } + return params, nil +} +func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) { + params, err := cli.encodeData(data) + if err != nil { + return nil, -1, err + } req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params) if err != nil { return nil, -1, err @@ -108,6 +126,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b } return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body)) } + return resp.Body, resp.StatusCode, nil } @@ -120,7 +139,7 @@ func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in in = bytes.NewReader([]byte{}) } - req, err := http.NewRequest(method, fmt.Sprintf("http://v%s%s", api.APIVERSION, path), in) + req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in) if err != nil { return err } @@ -157,14 +176,14 @@ func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in } if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") { - return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.terminalFd, cli.isTerminal) + return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut) } if stdout != nil || stderr != nil { // When TTY is ON, use regular copy if setRawTerminal { _, err = io.Copy(stdout, resp.Body) } else { - _, err = utils.StdCopy(stdout, stderr, resp.Body) + _, err = stdcopy.StdCopy(stdout, stderr, resp.Body) } log.Debugf("[stream] End of stdout") return err @@ -172,7 +191,7 @@ func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in return nil } -func (cli *DockerCli) resizeTty(id string) { +func (cli *DockerCli) resizeTty(id string, isExec bool) { height, width := cli.getTtySize() if height == 0 && width == 0 { return @@ -180,7 +199,15 @@ func (cli *DockerCli) resizeTty(id string) { v := url.Values{} v.Set("h", strconv.Itoa(height)) v.Set("w", strconv.Itoa(width)) - if _, _, err := readBody(cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil, false)); err != nil { + + path := "" + if !isExec { + path = "/containers/" + id + "/resize?" + } else { + path = "/exec/" + id + "/resize?" + } + + if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, false)); err != nil { log.Debugf("Error resize: %s", err) } } @@ -219,24 +246,24 @@ func getExitCode(cli *DockerCli, containerId string) (bool, int, error) { return state.GetBool("Running"), state.GetInt("ExitCode"), nil } -func (cli *DockerCli) monitorTtySize(id string) error { - cli.resizeTty(id) +func (cli *DockerCli) monitorTtySize(id string, isExec bool) error { + cli.resizeTty(id, isExec) sigchan := make(chan os.Signal, 1) gosignal.Notify(sigchan, syscall.SIGWINCH) go func() { for _ = range sigchan { - cli.resizeTty(id) + cli.resizeTty(id, isExec) } }() return nil } func (cli *DockerCli) getTtySize() (int, int) { - if !cli.isTerminal { + if !cli.isTerminalOut { return 0, 0 } - ws, err := term.GetWinsize(cli.terminalFd) + ws, err := term.GetWinsize(cli.outFd) if err != nil { log.Debugf("Error getting size: %s", err) if ws == nil { diff --git a/api/common.go b/api/common.go index 5cc33a9e1a..3eecaa0455 100644 --- a/api/common.go +++ b/api/common.go @@ -12,7 +12,7 @@ import ( ) const ( - APIVERSION version.Version = "1.14" + APIVERSION version.Version = "1.15" DEFAULTHTTPHOST = "127.0.0.1" DEFAULTUNIXSOCKET = "/var/run/docker.sock" ) diff --git a/api/server/MAINTAINERS b/api/server/MAINTAINERS index 310a159716..c92a061143 100644 --- a/api/server/MAINTAINERS +++ b/api/server/MAINTAINERS @@ -1,3 +1,2 @@ Victor Vieux (@vieux) -# off the grid until september -# Johan Euphrosine (@proppy) +Johan Euphrosine (@proppy) diff --git a/api/server/server.go b/api/server/server.go index 96f5bca6a6..897dd6142f 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -28,6 +28,7 @@ import ( "github.com/docker/docker/pkg/listenbuffer" "github.com/docker/docker/pkg/log" "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/systemd" "github.com/docker/docker/pkg/version" "github.com/docker/docker/registry" @@ -50,6 +51,24 @@ func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { return conn, conn, nil } +// Check to make sure request's Content-Type is application/json +func checkForJson(r *http.Request) error { + ct := r.Header.Get("Content-Type") + + // No Content-Type header is ok as long as there's no Body + if ct == "" { + if r.Body == nil || r.ContentLength == 0 { + return nil + } + } + + // Otherwise it better be json + if api.MatchesContentType(ct, "application/json") { + return nil + } + return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct) +} + //If we don't do this, POST method without Content-type (even with empty body) will fail func parseForm(r *http.Request) error { if r == nil { @@ -397,8 +416,8 @@ func getContainersLogs(eng *engine.Engine, version version.Version, w http.Respo outStream = utils.NewWriteFlusher(w) if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") { - errStream = utils.NewStdWriter(outStream, utils.Stderr) - outStream = utils.NewStdWriter(outStream, utils.Stdout) + errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) + outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } else { errStream = outStream } @@ -438,6 +457,11 @@ func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWrit job = eng.Job("commit", r.Form.Get("container")) stdoutBuffer = bytes.NewBuffer(nil) ) + + if err := checkForJson(r); err != nil { + return err + } + if err := config.Decode(r.Body); err != nil { log.Errorf("%s", err) } @@ -611,10 +635,18 @@ func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWr if vars == nil { return fmt.Errorf("Missing parameter") } + if err := parseForm(r); err != nil { + return err + } if version.GreaterThan("1.0") { w.Header().Set("Content-Type", "application/x-tar") } - job := eng.Job("image_export", vars["name"]) + var job *engine.Job + if name, ok := vars["name"]; ok { + job = eng.Job("image_export", name) + } else { + job = eng.Job("image_export", r.Form["names"]...) + } job.Stdout.Add(w) return job.Run() } @@ -636,6 +668,11 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re stdoutBuffer = bytes.NewBuffer(nil) warnings = bytes.NewBuffer(nil) ) + + if err := checkForJson(r); err != nil { + return err + } + if err := job.DecodeEnv(r.Body); err != nil { return err } @@ -653,6 +690,7 @@ func postContainersCreate(eng *engine.Engine, version version.Version, w http.Re } out.Set("Id", engine.Tail(stdoutBuffer, 1)) out.SetList("Warnings", outWarnings) + return writeJSON(w, http.StatusCreated, out) } @@ -679,7 +717,7 @@ func deleteContainers(eng *engine.Engine, version version.Version, w http.Respon if vars == nil { return fmt.Errorf("Missing parameter") } - job := eng.Job("delete", vars["name"]) + job := eng.Job("rm", vars["name"]) job.Setenv("forceRemove", r.Form.Get("force")) @@ -716,10 +754,15 @@ func postContainersStart(eng *engine.Engine, version version.Version, w http.Res job = eng.Job("start", name) ) + // If contentLength is -1, we can assumed chunked encoding + // or more technically that the length is unknown + // http://golang.org/src/pkg/net/http/request.go#L139 + // net/http otherwise seems to swallow any headers related to chunked encoding + // including r.TransferEncoding // allow a nil body for backwards compatibility - if r.Body != nil && r.ContentLength > 0 { - if !api.MatchesContentType(r.Header.Get("Content-Type"), "application/json") { - return fmt.Errorf("Content-Type of application/json is required") + if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) { + if err := checkForJson(r); err != nil { + return err } if err := job.DecodeEnv(r.Body); err != nil { @@ -832,8 +875,8 @@ func postContainersAttach(eng *engine.Engine, version version.Version, w http.Re fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") { - errStream = utils.NewStdWriter(outStream, utils.Stderr) - outStream = utils.NewStdWriter(outStream, utils.Stdout) + errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) + outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } else { errStream = outStream } @@ -984,12 +1027,12 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp var copyData engine.Env - if contentType := r.Header.Get("Content-Type"); api.MatchesContentType(contentType, "application/json") { - if err := copyData.Decode(r.Body); err != nil { - return err - } - } else { - return fmt.Errorf("Content-Type not supported: %s", contentType) + if err := checkForJson(r); err != nil { + return err + } + + if err := copyData.Decode(r.Body); err != nil { + return err } if copyData.Get("Resource") == "" { @@ -1004,6 +1047,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp job := eng.Job("container_copy", vars["name"], copyData.Get("Resource")) job.Stdout.Add(w) + w.Header().Set("Content-Type", "application/x-tar") if err := job.Run(); err != nil { log.Errorf("%s", err.Error()) if strings.Contains(err.Error(), "No such container") { @@ -1015,6 +1059,107 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp return nil } +func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := parseForm(r); err != nil { + return nil + } + var ( + out engine.Env + name = vars["name"] + job = eng.Job("execCreate", name) + stdoutBuffer = bytes.NewBuffer(nil) + ) + + if err := job.DecodeEnv(r.Body); err != nil { + return err + } + + job.Stdout.Add(stdoutBuffer) + // Register an instance of Exec in container. + if err := job.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err) + return err + } + // Return the ID + out.Set("Id", engine.Tail(stdoutBuffer, 1)) + + return writeJSON(w, http.StatusCreated, out) +} + +// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. +func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := parseForm(r); err != nil { + return nil + } + var ( + name = vars["name"] + job = eng.Job("execStart", name) + errOut io.Writer = os.Stderr + ) + + if err := job.DecodeEnv(r.Body); err != nil { + return err + } + if !job.GetenvBool("Detach") { + // Setting up the streaming http interface. + inStream, outStream, err := hijackServer(w) + if err != nil { + return err + } + + defer func() { + if tcpc, ok := inStream.(*net.TCPConn); ok { + tcpc.CloseWrite() + } else { + inStream.Close() + } + }() + defer func() { + if tcpc, ok := outStream.(*net.TCPConn); ok { + tcpc.CloseWrite() + } else if closer, ok := outStream.(io.Closer); ok { + closer.Close() + } + }() + + var errStream io.Writer + + fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") + if !job.GetenvBool("Tty") && version.GreaterThanOrEqualTo("1.6") { + errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) + outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) + } else { + errStream = outStream + } + job.Stdin.Add(inStream) + job.Stdout.Add(outStream) + job.Stderr.Set(errStream) + errOut = outStream + } + // Now run the user process in container. + job.SetCloseIO(false) + if err := job.Run(); err != nil { + fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err) + return err + } + w.WriteHeader(http.StatusNoContent) + + return nil +} + +func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := parseForm(r); err != nil { + return err + } + if vars == nil { + return fmt.Errorf("Missing parameter") + } + if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil { + return err + } + return nil +} + func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { w.WriteHeader(http.StatusOK) return nil @@ -1105,6 +1250,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st "/images/json": getImagesJSON, "/images/viz": getImagesViz, "/images/search": getImagesSearch, + "/images/get": getImagesGet, "/images/{name:.*}/get": getImagesGet, "/images/{name:.*}/history": getImagesHistory, "/images/{name:.*}/json": getImagesByName, @@ -1136,6 +1282,9 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st "/containers/{name:.*}/resize": postContainersResize, "/containers/{name:.*}/attach": postContainersAttach, "/containers/{name:.*}/copy": postContainersCopy, + "/containers/{name:.*}/exec": postContainerExecCreate, + "/exec/{name:.*}/start": postContainerExecStart, + "/exec/{name:.*}/resize": postContainerExecResize, }, "DELETE": { "/containers/{name:.*}": deleteContainers, @@ -1209,7 +1358,7 @@ func ServeFd(addr string, handle http.Handler) error { }() } - for i := 0; i < len(ls); i += 1 { + for i := 0; i < len(ls); i++ { err := <-chErrors if err != nil { return err @@ -1322,6 +1471,7 @@ func ListenAndServe(proto, addr string, job *engine.Job) error { return err } } + } if err := os.Chmod(addr, 0660); err != nil { return err @@ -1357,7 +1507,7 @@ func ServeApi(job *engine.Job) engine.Status { }() } - for i := 0; i < len(protoAddrs); i += 1 { + for i := 0; i < len(protoAddrs); i++ { err := <-chErrors if err != nil { return job.Error(err) diff --git a/api/server/server_unit_test.go b/api/server/server_unit_test.go index 950fea51d4..519652f377 100644 --- a/api/server/server_unit_test.go +++ b/api/server/server_unit_test.go @@ -455,7 +455,7 @@ func TestDeleteContainers(t *testing.T) { eng := engine.New() name := "foo" var called bool - eng.Register("delete", func(job *engine.Job) engine.Status { + eng.Register("rm", func(job *engine.Job) engine.Status { called = true if len(job.Args) == 0 { t.Fatalf("Job arguments is empty") diff --git a/archive/README.md b/archive/README.md deleted file mode 100644 index 4eb0c04181..0000000000 --- a/archive/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This code provides helper functions for dealing with archive files. - -**TODO**: Move this to either `pkg` or (if not possible) to `utils`. diff --git a/archive/common.go b/archive/common.go deleted file mode 100644 index 2aac34e840..0000000000 --- a/archive/common.go +++ /dev/null @@ -1,4 +0,0 @@ -package archive - -const twBufSize = 32 * 1024 -const trBufSize = 32 * 1024 diff --git a/builder/MAINTAINERS b/builder/MAINTAINERS new file mode 100644 index 0000000000..4d158aa20e --- /dev/null +++ b/builder/MAINTAINERS @@ -0,0 +1,2 @@ +Tibor Vass (@tiborvass) +Erik Hollensbe (@erikh) diff --git a/builder/dispatchers.go b/builder/dispatchers.go new file mode 100644 index 0000000000..82bb6ce5fd --- /dev/null +++ b/builder/dispatchers.go @@ -0,0 +1,352 @@ +package builder + +// This file contains the dispatchers for each command. Note that +// `nullDispatch` is not actually a command, but support for commands we parse +// but do nothing with. +// +// See evaluator.go for a higher level discussion of the whole evaluator +// package. + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/docker/docker/nat" + "github.com/docker/docker/pkg/log" + flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/runconfig" +) + +// dispatch with no layer / parsing. This is effectively not a command. +func nullDispatch(b *Builder, args []string, attributes map[string]bool, original string) error { + return nil +} + +// ENV foo bar +// +// Sets the environment variable foo to bar, also makes interpolation +// in the dockerfile available from the next statement on via ${foo}. +// +func env(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) != 2 { + return fmt.Errorf("ENV accepts two arguments") + } + + fullEnv := fmt.Sprintf("%s=%s", args[0], args[1]) + + for i, envVar := range b.Config.Env { + envParts := strings.SplitN(envVar, "=", 2) + if args[0] == envParts[0] { + b.Config.Env[i] = fullEnv + return b.commit("", b.Config.Cmd, fmt.Sprintf("ENV %s", fullEnv)) + } + } + b.Config.Env = append(b.Config.Env, fullEnv) + return b.commit("", b.Config.Cmd, fmt.Sprintf("ENV %s", fullEnv)) +} + +// MAINTAINER some text +// +// Sets the maintainer metadata. +func maintainer(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) != 1 { + return fmt.Errorf("MAINTAINER requires only one argument") + } + + b.maintainer = args[0] + return b.commit("", b.Config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer)) +} + +// ADD foo /path +// +// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling +// exist here. If you do not wish to have this automatic handling, use COPY. +// +func add(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) < 2 { + return fmt.Errorf("ADD requires at least two arguments") + } + + return b.runContextCommand(args, true, true, "ADD") +} + +// COPY foo /path +// +// Same as 'ADD' but without the tar and remote url handling. +// +func dispatchCopy(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) < 2 { + return fmt.Errorf("COPY requires at least two arguments") + } + + return b.runContextCommand(args, false, false, "COPY") +} + +// FROM imagename +// +// This sets the image the dockerfile will build on top of. +// +func from(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) != 1 { + return fmt.Errorf("FROM requires one argument") + } + + name := args[0] + + image, err := b.Daemon.Repositories().LookupImage(name) + if err != nil { + if b.Daemon.Graph().IsNotExist(err) { + image, err = b.pullImage(name) + } + + // note that the top level err will still be !nil here if IsNotExist is + // not the error. This approach just simplifies hte logic a bit. + if err != nil { + return err + } + } + + return b.processImageFrom(image) +} + +// ONBUILD RUN echo yo +// +// ONBUILD triggers run when the image is used in a FROM statement. +// +// ONBUILD handling has a lot of special-case functionality, the heading in +// evaluator.go and comments around dispatch() in the same file explain the +// special cases. search for 'OnBuild' in internals.go for additional special +// cases. +// +func onbuild(b *Builder, args []string, attributes map[string]bool, original string) error { + triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0])) + switch triggerInstruction { + case "ONBUILD": + return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed") + case "MAINTAINER", "FROM": + return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction) + } + + original = strings.TrimSpace(strings.TrimLeft(original, "ONBUILD")) + + b.Config.OnBuild = append(b.Config.OnBuild, original) + return b.commit("", b.Config.Cmd, fmt.Sprintf("ONBUILD %s", original)) +} + +// WORKDIR /tmp +// +// Set the working directory for future RUN/CMD/etc statements. +// +func workdir(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) != 1 { + return fmt.Errorf("WORKDIR requires exactly one argument") + } + + workdir := args[0] + + if workdir[0] == '/' { + b.Config.WorkingDir = workdir + } else { + if b.Config.WorkingDir == "" { + b.Config.WorkingDir = "/" + } + b.Config.WorkingDir = filepath.Join(b.Config.WorkingDir, workdir) + } + + return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir)) +} + +// RUN some command yo +// +// run a command and commit the image. Args are automatically prepended with +// 'sh -c' in the event there is only one argument. The difference in +// processing: +// +// RUN echo hi # sh -c echo hi +// RUN [ "echo", "hi" ] # echo hi +// +func run(b *Builder, args []string, attributes map[string]bool, original string) error { + if b.image == "" { + return fmt.Errorf("Please provide a source image with `from` prior to run") + } + + args = handleJsonArgs(args, attributes) + + if len(args) == 1 { + args = append([]string{"/bin/sh", "-c"}, args[0]) + } + + runCmd := flag.NewFlagSet("run", flag.ContinueOnError) + runCmd.SetOutput(ioutil.Discard) + runCmd.Usage = nil + + config, _, _, err := runconfig.Parse(runCmd, append([]string{b.image}, args...), nil) + if err != nil { + return err + } + + cmd := b.Config.Cmd + // set Cmd manually, this is special case only for Dockerfiles + b.Config.Cmd = config.Cmd + runconfig.Merge(b.Config, config) + + defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) + + log.Debugf("Command to be executed: %v", b.Config.Cmd) + + hit, err := b.probeCache() + if err != nil { + return err + } + if hit { + return nil + } + + c, err := b.create() + if err != nil { + return err + } + + // Ensure that we keep the container mounted until the commit + // to avoid unmounting and then mounting directly again + c.Mount() + defer c.Unmount() + + err = b.run(c) + if err != nil { + return err + } + if err := b.commit(c.ID, cmd, "run"); err != nil { + return err + } + + return nil +} + +// CMD foo +// +// Set the default command to run in the container (which may be empty). +// Argument handling is the same as RUN. +// +func cmd(b *Builder, args []string, attributes map[string]bool, original string) error { + b.Config.Cmd = handleJsonArgs(args, attributes) + + if !attributes["json"] && len(b.Config.Entrypoint) == 0 { + b.Config.Cmd = append([]string{"/bin/sh", "-c"}, b.Config.Cmd...) + } + + if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %v", b.Config.Cmd)); err != nil { + return err + } + + if len(args) != 0 { + b.cmdSet = true + } + + return nil +} + +// ENTRYPOINT /usr/sbin/nginx +// +// Set the entrypoint (which defaults to sh -c) to /usr/sbin/nginx. Will +// accept the CMD as the arguments to /usr/sbin/nginx. +// +// Handles command processing similar to CMD and RUN, only b.Config.Entrypoint +// is initialized at NewBuilder time instead of through argument parsing. +// +func entrypoint(b *Builder, args []string, attributes map[string]bool, original string) error { + parsed := handleJsonArgs(args, attributes) + + switch { + case len(parsed) == 0: + // ENTYRPOINT [] + b.Config.Entrypoint = nil + case attributes["json"]: + // ENTRYPOINT ["echo", "hi"] + b.Config.Entrypoint = parsed + default: + // ENTYRPOINT echo hi + b.Config.Entrypoint = []string{"/bin/sh", "-c", parsed[0]} + } + + // when setting the entrypoint if a CMD was not explicitly set then + // set the command to nil + if !b.cmdSet { + b.Config.Cmd = nil + } + + if err := b.commit("", b.Config.Cmd, fmt.Sprintf("ENTRYPOINT %v", b.Config.Entrypoint)); err != nil { + return err + } + + return nil +} + +// EXPOSE 6667/tcp 7000/tcp +// +// Expose ports for links and port mappings. This all ends up in +// b.Config.ExposedPorts for runconfig. +// +func expose(b *Builder, args []string, attributes map[string]bool, original string) error { + portsTab := args + + if b.Config.ExposedPorts == nil { + b.Config.ExposedPorts = make(nat.PortSet) + } + + ports, _, err := nat.ParsePortSpecs(append(portsTab, b.Config.PortSpecs...)) + if err != nil { + return err + } + + for port := range ports { + if _, exists := b.Config.ExposedPorts[port]; !exists { + b.Config.ExposedPorts[port] = struct{}{} + } + } + b.Config.PortSpecs = nil + + return b.commit("", b.Config.Cmd, fmt.Sprintf("EXPOSE %v", ports)) +} + +// USER foo +// +// Set the user to 'foo' for future commands and when running the +// ENTRYPOINT/CMD at container run time. +// +func user(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) != 1 { + return fmt.Errorf("USER requires exactly one argument") + } + + b.Config.User = args[0] + return b.commit("", b.Config.Cmd, fmt.Sprintf("USER %v", args)) +} + +// VOLUME /foo +// +// Expose the volume /foo for use. Will also accept the JSON array form. +// +func volume(b *Builder, args []string, attributes map[string]bool, original string) error { + if len(args) == 0 { + return fmt.Errorf("Volume cannot be empty") + } + + if b.Config.Volumes == nil { + b.Config.Volumes = map[string]struct{}{} + } + for _, v := range args { + b.Config.Volumes[v] = struct{}{} + } + if err := b.commit("", b.Config.Cmd, fmt.Sprintf("VOLUME %v", args)); err != nil { + return err + } + return nil +} + +// INSERT is no longer accepted, but we still parse it. +func insert(b *Builder, args []string, attributes map[string]bool, original string) error { + return fmt.Errorf("INSERT has been deprecated. Please use ADD instead") +} diff --git a/builder/evaluator.go b/builder/evaluator.go new file mode 100644 index 0000000000..4122616350 --- /dev/null +++ b/builder/evaluator.go @@ -0,0 +1,220 @@ +// builder is the evaluation step in the Dockerfile parse/evaluate pipeline. +// +// It incorporates a dispatch table based on the parser.Node values (see the +// parser package for more information) that are yielded from the parser itself. +// Calling NewBuilder with the BuildOpts struct can be used to customize the +// experience for execution purposes only. Parsing is controlled in the parser +// package, and this division of resposibility should be respected. +// +// Please see the jump table targets for the actual invocations, most of which +// will call out to the functions in internals.go to deal with their tasks. +// +// ONBUILD is a special case, which is covered in the onbuild() func in +// dispatchers.go. +// +// The evaluator uses the concept of "steps", which are usually each processable +// line in the Dockerfile. Each step is numbered and certain actions are taken +// before and after each step, such as creating an image ID and removing temporary +// containers and images. Note that ONBUILD creates a kinda-sorta "sub run" which +// includes its own set of steps (usually only one of them). +package builder + +import ( + "errors" + "fmt" + "io" + "os" + "path" + "strings" + + "github.com/docker/docker/builder/parser" + "github.com/docker/docker/daemon" + "github.com/docker/docker/engine" + "github.com/docker/docker/pkg/log" + "github.com/docker/docker/pkg/tarsum" + "github.com/docker/docker/registry" + "github.com/docker/docker/runconfig" + "github.com/docker/docker/utils" +) + +var ( + ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty") +) + +var evaluateTable map[string]func(*Builder, []string, map[string]bool, string) error + +func init() { + evaluateTable = map[string]func(*Builder, []string, map[string]bool, string) error{ + "env": env, + "maintainer": maintainer, + "add": add, + "copy": dispatchCopy, // copy() is a go builtin + "from": from, + "onbuild": onbuild, + "workdir": workdir, + "run": run, + "cmd": cmd, + "entrypoint": entrypoint, + "expose": expose, + "volume": volume, + "user": user, + "insert": insert, + } +} + +// internal struct, used to maintain configuration of the Dockerfile's +// processing as it evaluates the parsing result. +type Builder struct { + Daemon *daemon.Daemon + Engine *engine.Engine + + // effectively stdio for the run. Because it is not stdio, I said + // "Effectively". Do not use stdio anywhere in this package for any reason. + OutStream io.Writer + ErrStream io.Writer + + Verbose bool + UtilizeCache bool + + // controls how images and containers are handled between steps. + Remove bool + ForceRemove bool + + AuthConfig *registry.AuthConfig + AuthConfigFile *registry.ConfigFile + + // Deprecated, original writer used for ImagePull. To be removed. + OutOld io.Writer + StreamFormatter *utils.StreamFormatter + + Config *runconfig.Config // runconfig for cmd, run, entrypoint etc. + + // both of these are controlled by the Remove and ForceRemove options in BuildOpts + TmpContainers map[string]struct{} // a map of containers used for removes + + dockerfile *parser.Node // the syntax tree of the dockerfile + image string // image name for commit processing + maintainer string // maintainer name. could probably be removed. + cmdSet bool // indicates is CMD was set in current Dockerfile + context tarsum.TarSum // the context is a tarball that is uploaded by the client + contextPath string // the path of the temporary directory the local context is unpacked to (server side) + +} + +// Run the builder with the context. This is the lynchpin of this package. This +// will (barring errors): +// +// * call readContext() which will set up the temporary directory and unpack +// the context into it. +// * read the dockerfile +// * parse the dockerfile +// * walk the parse tree and execute it by dispatching to handlers. If Remove +// or ForceRemove is set, additional cleanup around containers happens after +// processing. +// * Print a happy message and return the image ID. +// +func (b *Builder) Run(context io.Reader) (string, error) { + if err := b.readContext(context); err != nil { + return "", err + } + + defer func() { + if err := os.RemoveAll(b.contextPath); err != nil { + log.Debugf("[BUILDER] failed to remove temporary context: %s", err) + } + }() + + filename := path.Join(b.contextPath, "Dockerfile") + + fi, err := os.Stat(filename) + if os.IsNotExist(err) { + return "", fmt.Errorf("Cannot build a directory without a Dockerfile") + } + if fi.Size() == 0 { + return "", ErrDockerfileEmpty + } + + f, err := os.Open(filename) + if err != nil { + return "", err + } + + defer f.Close() + + ast, err := parser.Parse(f) + if err != nil { + return "", err + } + + b.dockerfile = ast + + // some initializations that would not have been supplied by the caller. + b.Config = &runconfig.Config{Entrypoint: []string{}, Cmd: nil} + b.TmpContainers = map[string]struct{}{} + + for i, n := range b.dockerfile.Children { + if err := b.dispatch(i, n); err != nil { + if b.ForceRemove { + b.clearTmp() + } + return "", err + } + fmt.Fprintf(b.OutStream, " ---> %s\n", utils.TruncateID(b.image)) + if b.Remove { + b.clearTmp() + } + } + + if b.image == "" { + return "", fmt.Errorf("No image was generated. Is your Dockerfile empty?\n") + } + + fmt.Fprintf(b.OutStream, "Successfully built %s\n", utils.TruncateID(b.image)) + return b.image, nil +} + +// This method is the entrypoint to all statement handling routines. +// +// Almost all nodes will have this structure: +// Child[Node, Node, Node] where Child is from parser.Node.Children and each +// node comes from parser.Node.Next. This forms a "line" with a statement and +// arguments and we process them in this normalized form by hitting +// evaluateTable with the leaf nodes of the command and the Builder object. +// +// ONBUILD is a special case; in this case the parser will emit: +// Child[Node, Child[Node, Node...]] where the first node is the literal +// "onbuild" and the child entrypoint is the command of the ONBUILD statmeent, +// such as `RUN` in ONBUILD RUN foo. There is special case logic in here to +// deal with that, at least until it becomes more of a general concern with new +// features. +func (b *Builder) dispatch(stepN int, ast *parser.Node) error { + cmd := ast.Value + attrs := ast.Attributes + original := ast.Original + strs := []string{} + msg := fmt.Sprintf("Step %d : %s", stepN, strings.ToUpper(cmd)) + + if cmd == "onbuild" { + ast = ast.Next.Children[0] + strs = append(strs, b.replaceEnv(ast.Value)) + msg += " " + ast.Value + } + + for ast.Next != nil { + ast = ast.Next + strs = append(strs, b.replaceEnv(ast.Value)) + msg += " " + ast.Value + } + + fmt.Fprintln(b.OutStream, msg) + + // XXX yes, we skip any cmds that are not valid; the parser should have + // picked these out already. + if f, ok := evaluateTable[cmd]; ok { + return f(b, strs, attrs, original) + } + + fmt.Fprintf(b.ErrStream, "# Skipping unknown instruction %s\n", strings.ToUpper(cmd)) + + return nil +} diff --git a/builder/internals.go b/builder/internals.go new file mode 100644 index 0000000000..5fd03f7745 --- /dev/null +++ b/builder/internals.go @@ -0,0 +1,687 @@ +package builder + +// internals for handling commands. Covers many areas and a lot of +// non-contiguous functionality. Please read the comments. + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "net/url" + "os" + "path" + "path/filepath" + "sort" + "strings" + "syscall" + "time" + + "github.com/docker/docker/builder/parser" + "github.com/docker/docker/daemon" + imagepkg "github.com/docker/docker/image" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/log" + "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/promise" + "github.com/docker/docker/pkg/symlink" + "github.com/docker/docker/pkg/system" + "github.com/docker/docker/pkg/tarsum" + "github.com/docker/docker/registry" + "github.com/docker/docker/utils" +) + +func (b *Builder) readContext(context io.Reader) error { + tmpdirPath, err := ioutil.TempDir("", "docker-build") + if err != nil { + return err + } + + decompressedStream, err := archive.DecompressStream(context) + if err != nil { + return err + } + + if b.context, err = tarsum.NewTarSum(decompressedStream, true, tarsum.Version0); err != nil { + return err + } + if err := archive.Untar(b.context, tmpdirPath, nil); err != nil { + return err + } + + b.contextPath = tmpdirPath + return nil +} + +func (b *Builder) commit(id string, autoCmd []string, comment string) error { + if b.image == "" { + return fmt.Errorf("Please provide a source image with `from` prior to commit") + } + b.Config.Image = b.image + if id == "" { + cmd := b.Config.Cmd + b.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment} + defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) + + hit, err := b.probeCache() + if err != nil { + return err + } + if hit { + return nil + } + + container, err := b.create() + if err != nil { + return err + } + id = container.ID + + if err := container.Mount(); err != nil { + return err + } + defer container.Unmount() + } + container := b.Daemon.Get(id) + if container == nil { + return fmt.Errorf("An error occured while creating the container") + } + + // Note: Actually copy the struct + autoConfig := *b.Config + autoConfig.Cmd = autoCmd + + // Commit the container + image, err := b.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig) + if err != nil { + return err + } + b.image = image.ID + return nil +} + +type copyInfo struct { + origPath string + destPath string + hash string + decompress bool + tmpDir string +} + +func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecompression bool, cmdName string) error { + if b.context == nil { + return fmt.Errorf("No context given. Impossible to use %s", cmdName) + } + + if len(args) < 2 { + return fmt.Errorf("Invalid %s format - at least two arguments required", cmdName) + } + + dest := args[len(args)-1] // last one is always the dest + + copyInfos := []*copyInfo{} + + b.Config.Image = b.image + + defer func() { + for _, ci := range copyInfos { + if ci.tmpDir != "" { + os.RemoveAll(ci.tmpDir) + } + } + }() + + // Loop through each src file and calculate the info we need to + // do the copy (e.g. hash value if cached). Don't actually do + // the copy until we've looked at all src files + for _, orig := range args[0 : len(args)-1] { + err := calcCopyInfo(b, cmdName, ©Infos, orig, dest, allowRemote, allowDecompression) + if err != nil { + return err + } + } + + if len(copyInfos) == 0 { + return fmt.Errorf("No source files were specified") + } + + if len(copyInfos) > 1 && !strings.HasSuffix(dest, "/") { + return fmt.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName) + } + + // For backwards compat, if there's just one CI then use it as the + // cache look-up string, otherwise hash 'em all into one + var srcHash string + var origPaths string + + if len(copyInfos) == 1 { + srcHash = copyInfos[0].hash + origPaths = copyInfos[0].origPath + } else { + var hashs []string + var origs []string + for _, ci := range copyInfos { + hashs = append(hashs, ci.hash) + origs = append(origs, ci.origPath) + } + hasher := sha256.New() + hasher.Write([]byte(strings.Join(hashs, ","))) + srcHash = "multi:" + hex.EncodeToString(hasher.Sum(nil)) + origPaths = strings.Join(origs, " ") + } + + cmd := b.Config.Cmd + b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest)} + defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) + + hit, err := b.probeCache() + if err != nil { + return err + } + // If we do not have at least one hash, never use the cache + if hit && b.UtilizeCache { + return nil + } + + container, _, err := b.Daemon.Create(b.Config, nil, "") + if err != nil { + return err + } + b.TmpContainers[container.ID] = struct{}{} + + if err := container.Mount(); err != nil { + return err + } + defer container.Unmount() + + for _, ci := range copyInfos { + if err := b.addContext(container, ci.origPath, ci.destPath, ci.decompress); err != nil { + return err + } + } + + if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil { + return err + } + return nil +} + +func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath string, destPath string, allowRemote bool, allowDecompression bool) error { + + if origPath != "" && origPath[0] == '/' && len(origPath) > 1 { + origPath = origPath[1:] + } + origPath = strings.TrimPrefix(origPath, "./") + + // In the remote/URL case, download it and gen its hashcode + if utils.IsURL(origPath) { + if !allowRemote { + return fmt.Errorf("Source can't be a URL for %s", cmdName) + } + + ci := copyInfo{} + ci.origPath = origPath + ci.hash = origPath // default to this but can change + ci.destPath = destPath + ci.decompress = false + *cInfos = append(*cInfos, &ci) + + // Initiate the download + resp, err := utils.Download(ci.origPath) + if err != nil { + return err + } + + // Create a tmp dir + tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote") + if err != nil { + return err + } + ci.tmpDir = tmpDirName + + // Create a tmp file within our tmp dir + tmpFileName := path.Join(tmpDirName, "tmp") + tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if err != nil { + return err + } + + // Download and dump result to tmp file + if _, err := io.Copy(tmpFile, utils.ProgressReader(resp.Body, int(resp.ContentLength), b.OutOld, b.StreamFormatter, true, "", "Downloading")); err != nil { + tmpFile.Close() + return err + } + fmt.Fprintf(b.OutStream, "\n") + tmpFile.Close() + + // Remove the mtime of the newly created tmp file + if err := system.UtimesNano(tmpFileName, make([]syscall.Timespec, 2)); err != nil { + return err + } + + ci.origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName)) + + // If the destination is a directory, figure out the filename. + if strings.HasSuffix(ci.destPath, "/") { + u, err := url.Parse(origPath) + if err != nil { + return err + } + path := u.Path + if strings.HasSuffix(path, "/") { + path = path[:len(path)-1] + } + parts := strings.Split(path, "/") + filename := parts[len(parts)-1] + if filename == "" { + return fmt.Errorf("cannot determine filename from url: %s", u) + } + ci.destPath = ci.destPath + filename + } + + // Calc the checksum, only if we're using the cache + if b.UtilizeCache { + r, err := archive.Tar(tmpFileName, archive.Uncompressed) + if err != nil { + return err + } + tarSum, err := tarsum.NewTarSum(r, true, tarsum.Version0) + if err != nil { + return err + } + if _, err := io.Copy(ioutil.Discard, tarSum); err != nil { + return err + } + ci.hash = tarSum.Sum(nil) + r.Close() + } + + return nil + } + + // Deal with wildcards + if ContainsWildcards(origPath) { + for _, fileInfo := range b.context.GetSums() { + if fileInfo.Name() == "" { + continue + } + match, _ := path.Match(origPath, fileInfo.Name()) + if !match { + continue + } + + calcCopyInfo(b, cmdName, cInfos, fileInfo.Name(), destPath, allowRemote, allowDecompression) + } + return nil + } + + // Must be a dir or a file + + if err := b.checkPathForAddition(origPath); err != nil { + return err + } + fi, _ := os.Stat(path.Join(b.contextPath, origPath)) + + ci := copyInfo{} + ci.origPath = origPath + ci.hash = origPath + ci.destPath = destPath + ci.decompress = allowDecompression + *cInfos = append(*cInfos, &ci) + + // If not using cache don't need to do anything else. + // If we are using a cache then calc the hash for the src file/dir + if !b.UtilizeCache { + return nil + } + + // Deal with the single file case + if !fi.IsDir() { + // This will match first file in sums of the archive + fis := b.context.GetSums().GetFile(ci.origPath) + if fis != nil { + ci.hash = "file:" + fis.Sum() + } + return nil + } + + // Must be a dir + var subfiles []string + absOrigPath := path.Join(b.contextPath, ci.origPath) + + // Add a trailing / to make sure we only pick up nested files under + // the dir and not sibling files of the dir that just happen to + // start with the same chars + if !strings.HasSuffix(absOrigPath, "/") { + absOrigPath += "/" + } + + // Need path w/o / too to find matching dir w/o trailing / + absOrigPathNoSlash := absOrigPath[:len(absOrigPath)-1] + + for _, fileInfo := range b.context.GetSums() { + absFile := path.Join(b.contextPath, fileInfo.Name()) + if strings.HasPrefix(absFile, absOrigPath) || absFile == absOrigPathNoSlash { + subfiles = append(subfiles, fileInfo.Sum()) + } + } + sort.Strings(subfiles) + hasher := sha256.New() + hasher.Write([]byte(strings.Join(subfiles, ","))) + ci.hash = "dir:" + hex.EncodeToString(hasher.Sum(nil)) + + return nil +} + +func ContainsWildcards(name string) bool { + for i := 0; i < len(name); i++ { + ch := name[i] + if ch == '\\' { + i++ + } else if ch == '*' || ch == '?' || ch == '[' { + return true + } + } + return false +} + +func (b *Builder) pullImage(name string) (*imagepkg.Image, error) { + remote, tag := parsers.ParseRepositoryTag(name) + if tag == "" { + tag = "latest" + } + pullRegistryAuth := b.AuthConfig + if len(b.AuthConfigFile.Configs) > 0 { + // The request came with a full auth config file, we prefer to use that + endpoint, _, err := registry.ResolveRepositoryName(remote) + if err != nil { + return nil, err + } + resolvedAuth := b.AuthConfigFile.ResolveAuthConfig(endpoint) + pullRegistryAuth = &resolvedAuth + } + job := b.Engine.Job("pull", remote, tag) + job.SetenvBool("json", b.StreamFormatter.Json()) + job.SetenvBool("parallel", true) + job.SetenvJson("authConfig", pullRegistryAuth) + job.Stdout.Add(b.OutOld) + if err := job.Run(); err != nil { + return nil, err + } + image, err := b.Daemon.Repositories().LookupImage(name) + if err != nil { + return nil, err + } + + return image, nil +} + +func (b *Builder) processImageFrom(img *imagepkg.Image) error { + b.image = img.ID + + if img.Config != nil { + b.Config = img.Config + } + + if len(b.Config.Env) == 0 { + b.Config.Env = append(b.Config.Env, "PATH="+daemon.DefaultPathEnv) + } + + // Process ONBUILD triggers if they exist + if nTriggers := len(b.Config.OnBuild); nTriggers != 0 { + fmt.Fprintf(b.ErrStream, "# Executing %d build triggers\n", nTriggers) + } + + // Copy the ONBUILD triggers, and remove them from the config, since the config will be commited. + onBuildTriggers := b.Config.OnBuild + b.Config.OnBuild = []string{} + + // parse the ONBUILD triggers by invoking the parser + for stepN, step := range onBuildTriggers { + ast, err := parser.Parse(strings.NewReader(step)) + if err != nil { + return err + } + + for i, n := range ast.Children { + switch strings.ToUpper(n.Value) { + case "ONBUILD": + return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed") + case "MAINTAINER", "FROM": + return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", n.Value) + } + + fmt.Fprintf(b.OutStream, "Trigger %d, %s\n", stepN, step) + + if err := b.dispatch(i, n); err != nil { + return err + } + } + } + + return nil +} + +// probeCache checks to see if image-caching is enabled (`b.UtilizeCache`) +// and if so attempts to look up the current `b.image` and `b.Config` pair +// in the current server `b.Daemon`. If an image is found, probeCache returns +// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there +// is any error, it returns `(false, err)`. +func (b *Builder) probeCache() (bool, error) { + if b.UtilizeCache { + if cache, err := b.Daemon.ImageGetCached(b.image, b.Config); err != nil { + return false, err + } else if cache != nil { + fmt.Fprintf(b.OutStream, " ---> Using cache\n") + log.Debugf("[BUILDER] Use cached version") + b.image = cache.ID + return true, nil + } else { + log.Debugf("[BUILDER] Cache miss") + } + } + return false, nil +} + +func (b *Builder) create() (*daemon.Container, error) { + if b.image == "" { + return nil, fmt.Errorf("Please provide a source image with `from` prior to run") + } + b.Config.Image = b.image + + config := *b.Config + + // Create the container + c, warnings, err := b.Daemon.Create(b.Config, nil, "") + if err != nil { + return nil, err + } + for _, warning := range warnings { + fmt.Fprintf(b.OutStream, " ---> [Warning] %s\n", warning) + } + + b.TmpContainers[c.ID] = struct{}{} + fmt.Fprintf(b.OutStream, " ---> Running in %s\n", utils.TruncateID(c.ID)) + + // override the entry point that may have been picked up from the base image + c.Path = config.Cmd[0] + c.Args = config.Cmd[1:] + + return c, nil +} + +func (b *Builder) run(c *daemon.Container) error { + var errCh chan error + if b.Verbose { + errCh = promise.Go(func() error { + // FIXME: call the 'attach' job so that daemon.Attach can be made private + // + // FIXME (LK4D4): Also, maybe makes sense to call "logs" job, it is like attach + // but without hijacking for stdin. Also, with attach there can be race + // condition because of some output already was printed before it. + return <-b.Daemon.Attach(&c.StreamConfig, c.Config.OpenStdin, c.Config.StdinOnce, c.Config.Tty, nil, nil, b.OutStream, b.ErrStream) + }) + } + + //start the container + if err := c.Start(); err != nil { + return err + } + + if errCh != nil { + if err := <-errCh; err != nil { + return err + } + } + + // Wait for it to finish + if ret, _ := c.WaitStop(-1 * time.Second); ret != 0 { + err := &utils.JSONError{ + Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.Config.Cmd, ret), + Code: ret, + } + return err + } + + return nil +} + +func (b *Builder) checkPathForAddition(orig string) error { + origPath := path.Join(b.contextPath, orig) + origPath, err := filepath.EvalSymlinks(origPath) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("%s: no such file or directory", orig) + } + return err + } + if !strings.HasPrefix(origPath, b.contextPath) { + return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath) + } + if _, err := os.Stat(origPath); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("%s: no such file or directory", orig) + } + return err + } + return nil +} + +func (b *Builder) addContext(container *daemon.Container, orig, dest string, decompress bool) error { + var ( + err error + destExists = true + origPath = path.Join(b.contextPath, orig) + destPath = path.Join(container.RootfsPath(), dest) + ) + + if destPath != container.RootfsPath() { + destPath, err = symlink.FollowSymlinkInScope(destPath, container.RootfsPath()) + if err != nil { + return err + } + } + + // Preserve the trailing '/' + if strings.HasSuffix(dest, "/") || dest == "." { + destPath = destPath + "/" + } + + destStat, err := os.Stat(destPath) + if err != nil { + if !os.IsNotExist(err) { + return err + } + destExists = false + } + + fi, err := os.Stat(origPath) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("%s: no such file or directory", orig) + } + return err + } + + if fi.IsDir() { + return copyAsDirectory(origPath, destPath, destExists) + } + + // If we are adding a remote file (or we've been told not to decompress), do not try to untar it + if decompress { + // First try to unpack the source as an archive + // to support the untar feature we need to clean up the path a little bit + // because tar is very forgiving. First we need to strip off the archive's + // filename from the path but this is only added if it does not end in / . + tarDest := destPath + if strings.HasSuffix(tarDest, "/") { + tarDest = filepath.Dir(destPath) + } + + // try to successfully untar the orig + if err := archive.UntarPath(origPath, tarDest); err == nil { + return nil + } else if err != io.EOF { + log.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err) + } + } + + if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil { + return err + } + if err := archive.CopyWithTar(origPath, destPath); err != nil { + return err + } + + resPath := destPath + if destExists && destStat.IsDir() { + resPath = path.Join(destPath, path.Base(origPath)) + } + + return fixPermissions(resPath, 0, 0) +} + +func copyAsDirectory(source, destination string, destinationExists bool) error { + if err := archive.CopyWithTar(source, destination); err != nil { + return err + } + + if destinationExists { + files, err := ioutil.ReadDir(source) + if err != nil { + return err + } + + for _, file := range files { + if err := fixPermissions(filepath.Join(destination, file.Name()), 0, 0); err != nil { + return err + } + } + return nil + } + + return fixPermissions(destination, 0, 0) +} + +func fixPermissions(destination string, uid, gid int) error { + return filepath.Walk(destination, func(path string, info os.FileInfo, err error) error { + if err := os.Lchown(path, uid, gid); err != nil && !os.IsNotExist(err) { + return err + } + return nil + }) +} + +func (b *Builder) clearTmp() { + for c := range b.TmpContainers { + tmp := b.Daemon.Get(c) + if err := b.Daemon.Destroy(tmp); err != nil { + fmt.Fprintf(b.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error()) + return + } + b.Daemon.DeleteVolumes(tmp.VolumePaths()) + delete(b.TmpContainers, c) + fmt.Fprintf(b.OutStream, "Removing intermediate container %s\n", utils.TruncateID(c)) + } +} diff --git a/builder/job.go b/builder/job.go new file mode 100644 index 0000000000..555232c9ae --- /dev/null +++ b/builder/job.go @@ -0,0 +1,130 @@ +package builder + +import ( + "io" + "io/ioutil" + "os" + "os/exec" + "strings" + + "github.com/docker/docker/daemon" + "github.com/docker/docker/engine" + "github.com/docker/docker/graph" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/registry" + "github.com/docker/docker/utils" +) + +type BuilderJob struct { + Engine *engine.Engine + Daemon *daemon.Daemon +} + +func (b *BuilderJob) Install() { + b.Engine.Register("build", b.CmdBuild) +} + +func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status { + if len(job.Args) != 0 { + return job.Errorf("Usage: %s\n", job.Name) + } + var ( + remoteURL = job.Getenv("remote") + repoName = job.Getenv("t") + suppressOutput = job.GetenvBool("q") + noCache = job.GetenvBool("nocache") + rm = job.GetenvBool("rm") + forceRm = job.GetenvBool("forcerm") + authConfig = ®istry.AuthConfig{} + configFile = ®istry.ConfigFile{} + tag string + context io.ReadCloser + ) + job.GetenvJson("authConfig", authConfig) + job.GetenvJson("configFile", configFile) + + repoName, tag = parsers.ParseRepositoryTag(repoName) + if repoName != "" { + if _, _, err := registry.ResolveRepositoryName(repoName); err != nil { + return job.Error(err) + } + if len(tag) > 0 { + if err := graph.ValidateTagName(tag); err != nil { + return job.Error(err) + } + } + } + + if remoteURL == "" { + context = ioutil.NopCloser(job.Stdin) + } else if utils.IsGIT(remoteURL) { + if !strings.HasPrefix(remoteURL, "git://") { + remoteURL = "https://" + remoteURL + } + root, err := ioutil.TempDir("", "docker-build-git") + if err != nil { + return job.Error(err) + } + defer os.RemoveAll(root) + + if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil { + return job.Errorf("Error trying to use git: %s (%s)", err, output) + } + + c, err := archive.Tar(root, archive.Uncompressed) + if err != nil { + return job.Error(err) + } + context = c + } else if utils.IsURL(remoteURL) { + f, err := utils.Download(remoteURL) + if err != nil { + return job.Error(err) + } + defer f.Body.Close() + dockerFile, err := ioutil.ReadAll(f.Body) + if err != nil { + return job.Error(err) + } + c, err := archive.Generate("Dockerfile", string(dockerFile)) + if err != nil { + return job.Error(err) + } + context = c + } + defer context.Close() + + sf := utils.NewStreamFormatter(job.GetenvBool("json")) + + builder := &Builder{ + Daemon: b.Daemon, + Engine: b.Engine, + OutStream: &utils.StdoutFormater{ + Writer: job.Stdout, + StreamFormatter: sf, + }, + ErrStream: &utils.StderrFormater{ + Writer: job.Stdout, + StreamFormatter: sf, + }, + Verbose: !suppressOutput, + UtilizeCache: !noCache, + Remove: rm, + ForceRemove: forceRm, + OutOld: job.Stdout, + StreamFormatter: sf, + AuthConfig: authConfig, + AuthConfigFile: configFile, + } + + id, err := builder.Run(context) + if err != nil { + return job.Error(err) + } + + if repoName != "" { + b.Daemon.Repositories().Set(repoName, tag, id, false) + } + return engine.StatusOK +} diff --git a/builder/parser/dumper/main.go b/builder/parser/dumper/main.go new file mode 100644 index 0000000000..33202b7038 --- /dev/null +++ b/builder/parser/dumper/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "os" + + "github.com/docker/docker/builder/parser" +) + +func main() { + var f *os.File + var err error + + if len(os.Args) < 2 { + fmt.Println("please supply filename(s)") + os.Exit(1) + } + + for _, fn := range os.Args[1:] { + f, err = os.Open(fn) + if err != nil { + panic(err) + } + + ast, err := parser.Parse(f) + if err != nil { + panic(err) + } else { + fmt.Println(ast.Dump()) + } + } +} diff --git a/builder/parser/line_parsers.go b/builder/parser/line_parsers.go new file mode 100644 index 0000000000..358e2f73a0 --- /dev/null +++ b/builder/parser/line_parsers.go @@ -0,0 +1,155 @@ +package parser + +// line parsers are dispatch calls that parse a single unit of text into a +// Node object which contains the whole statement. Dockerfiles have varied +// (but not usually unique, see ONBUILD for a unique example) parsing rules +// per-command, and these unify the processing in a way that makes it +// manageable. + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" +) + +var ( + errDockerfileJSONNesting = errors.New("You may not nest arrays in Dockerfile statements.") +) + +// ignore the current argument. This will still leave a command parsed, but +// will not incorporate the arguments into the ast. +func parseIgnore(rest string) (*Node, map[string]bool, error) { + return &Node{}, nil, nil +} + +// used for onbuild. Could potentially be used for anything that represents a +// statement with sub-statements. +// +// ONBUILD RUN foo bar -> (onbuild (run foo bar)) +// +func parseSubCommand(rest string) (*Node, map[string]bool, error) { + _, child, err := parseLine(rest) + if err != nil { + return nil, nil, err + } + + return &Node{Children: []*Node{child}}, nil, nil +} + +// parse environment like statements. Note that this does *not* handle +// variable interpolation, which will be handled in the evaluator. +func parseEnv(rest string) (*Node, map[string]bool, error) { + node := &Node{} + rootnode := node + strs := TOKEN_WHITESPACE.Split(rest, 2) + + if len(strs) < 2 { + return nil, nil, fmt.Errorf("ENV must have two arguments") + } + + node.Value = strs[0] + node.Next = &Node{} + node.Next.Value = strs[1] + + return rootnode, nil, nil +} + +// parses a whitespace-delimited set of arguments. The result is effectively a +// linked list of string arguments. +func parseStringsWhitespaceDelimited(rest string) (*Node, map[string]bool, error) { + node := &Node{} + rootnode := node + prevnode := node + for _, str := range TOKEN_WHITESPACE.Split(rest, -1) { // use regexp + prevnode = node + node.Value = str + node.Next = &Node{} + node = node.Next + } + + // XXX to get around regexp.Split *always* providing an empty string at the + // end due to how our loop is constructed, nil out the last node in the + // chain. + prevnode.Next = nil + + return rootnode, nil, nil +} + +// parsestring just wraps the string in quotes and returns a working node. +func parseString(rest string) (*Node, map[string]bool, error) { + n := &Node{} + n.Value = rest + return n, nil, nil +} + +// parseJSON converts JSON arrays to an AST. +func parseJSON(rest string) (*Node, map[string]bool, error) { + var ( + myJson []interface{} + next = &Node{} + orignext = next + prevnode = next + ) + + if err := json.Unmarshal([]byte(rest), &myJson); err != nil { + return nil, nil, err + } + + for _, str := range myJson { + switch str.(type) { + case string: + case float64: + str = strconv.FormatFloat(str.(float64), 'G', -1, 64) + default: + return nil, nil, errDockerfileJSONNesting + } + next.Value = str.(string) + next.Next = &Node{} + prevnode = next + next = next.Next + } + + prevnode.Next = nil + + return orignext, map[string]bool{"json": true}, nil +} + +// parseMaybeJSON determines if the argument appears to be a JSON array. If +// so, passes to parseJSON; if not, quotes the result and returns a single +// node. +func parseMaybeJSON(rest string) (*Node, map[string]bool, error) { + rest = strings.TrimSpace(rest) + + node, attrs, err := parseJSON(rest) + + if err == nil { + return node, attrs, nil + } + if err == errDockerfileJSONNesting { + return nil, nil, err + } + + node = &Node{} + node.Value = rest + return node, nil, nil +} + +// parseMaybeJSONToList determines if the argument appears to be a JSON array. If +// so, passes to parseJSON; if not, attmpts to parse it as a whitespace +// delimited string. +func parseMaybeJSONToList(rest string) (*Node, map[string]bool, error) { + rest = strings.TrimSpace(rest) + + node, attrs, err := parseJSON(rest) + + if err == nil { + return node, attrs, nil + } + if err == errDockerfileJSONNesting { + return nil, nil, err + } + + return parseStringsWhitespaceDelimited(rest) +} diff --git a/builder/parser/parser.go b/builder/parser/parser.go new file mode 100644 index 0000000000..5e8bcb5a9c --- /dev/null +++ b/builder/parser/parser.go @@ -0,0 +1,139 @@ +// This package implements a parser and parse tree dumper for Dockerfiles. +package parser + +import ( + "bufio" + "io" + "regexp" + "strings" + "unicode" +) + +// Node is a structure used to represent a parse tree. +// +// In the node there are three fields, Value, Next, and Children. Value is the +// current token's string value. Next is always the next non-child token, and +// children contains all the children. Here's an example: +// +// (value next (child child-next child-next-next) next-next) +// +// This data structure is frankly pretty lousy for handling complex languages, +// but lucky for us the Dockerfile isn't very complicated. This structure +// works a little more effectively than a "proper" parse tree for our needs. +// +type Node struct { + Value string // actual content + Next *Node // the next item in the current sexp + Children []*Node // the children of this sexp + Attributes map[string]bool // special attributes for this node + Original string // original line used before parsing +} + +var ( + dispatch map[string]func(string) (*Node, map[string]bool, error) + TOKEN_WHITESPACE = regexp.MustCompile(`[\t\v\f\r ]+`) + TOKEN_LINE_CONTINUATION = regexp.MustCompile(`\\\s*$`) + TOKEN_COMMENT = regexp.MustCompile(`^#.*$`) +) + +func init() { + // Dispatch Table. see line_parsers.go for the parse functions. + // The command is parsed and mapped to the line parser. The line parser + // recieves the arguments but not the command, and returns an AST after + // reformulating the arguments according to the rules in the parser + // functions. Errors are propogated up by Parse() and the resulting AST can + // be incorporated directly into the existing AST as a next. + dispatch = map[string]func(string) (*Node, map[string]bool, error){ + "user": parseString, + "onbuild": parseSubCommand, + "workdir": parseString, + "env": parseEnv, + "maintainer": parseString, + "from": parseString, + "add": parseStringsWhitespaceDelimited, + "copy": parseStringsWhitespaceDelimited, + "run": parseMaybeJSON, + "cmd": parseMaybeJSON, + "entrypoint": parseMaybeJSON, + "expose": parseStringsWhitespaceDelimited, + "volume": parseMaybeJSONToList, + "insert": parseIgnore, + } +} + +// parse a line and return the remainder. +func parseLine(line string) (string, *Node, error) { + if line = stripComments(line); line == "" { + return "", nil, nil + } + + if TOKEN_LINE_CONTINUATION.MatchString(line) { + line = TOKEN_LINE_CONTINUATION.ReplaceAllString(line, "") + return line, nil, nil + } + + cmd, args, err := splitCommand(line) + if err != nil { + return "", nil, err + } + + node := &Node{} + node.Value = cmd + + sexp, attrs, err := fullDispatch(cmd, args) + if err != nil { + return "", nil, err + } + + if sexp.Value != "" || sexp.Next != nil || sexp.Children != nil { + node.Next = sexp + node.Attributes = attrs + node.Original = line + } + + return "", node, nil +} + +// The main parse routine. Handles an io.ReadWriteCloser and returns the root +// of the AST. +func Parse(rwc io.Reader) (*Node, error) { + root := &Node{} + scanner := bufio.NewScanner(rwc) + + for scanner.Scan() { + scannedLine := strings.TrimLeftFunc(scanner.Text(), unicode.IsSpace) + if stripComments(scannedLine) == "" { + continue + } + + line, child, err := parseLine(scannedLine) + if err != nil { + return nil, err + } + + if line != "" && child == nil { + for scanner.Scan() { + newline := scanner.Text() + + if stripComments(strings.TrimSpace(newline)) == "" { + continue + } + + line, child, err = parseLine(line + newline) + if err != nil { + return nil, err + } + + if child != nil { + break + } + } + } + + if child != nil { + root.Children = append(root.Children, child) + } + } + + return root, nil +} diff --git a/builder/parser/parser_test.go b/builder/parser/parser_test.go new file mode 100644 index 0000000000..1b517fcc14 --- /dev/null +++ b/builder/parser/parser_test.go @@ -0,0 +1,82 @@ +package parser + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +const testDir = "testfiles" +const negativeTestDir = "testfiles-negative" + +func getDirs(t *testing.T, dir string) []os.FileInfo { + f, err := os.Open(dir) + if err != nil { + t.Fatal(err) + } + + defer f.Close() + + dirs, err := f.Readdir(0) + if err != nil { + t.Fatal(err) + } + + return dirs +} + +func TestTestNegative(t *testing.T) { + for _, dir := range getDirs(t, negativeTestDir) { + dockerfile := filepath.Join(negativeTestDir, dir.Name(), "Dockerfile") + + df, err := os.Open(dockerfile) + if err != nil { + t.Fatalf("Dockerfile missing for %s: %s", dir.Name(), err.Error()) + } + + _, err = Parse(df) + if err == nil { + t.Fatalf("No error parsing broken dockerfile for %s", dir.Name()) + } + + df.Close() + } +} + +func TestTestData(t *testing.T) { + for _, dir := range getDirs(t, testDir) { + dockerfile := filepath.Join(testDir, dir.Name(), "Dockerfile") + resultfile := filepath.Join(testDir, dir.Name(), "result") + + df, err := os.Open(dockerfile) + if err != nil { + t.Fatalf("Dockerfile missing for %s: %s", dir.Name(), err.Error()) + } + + rf, err := os.Open(resultfile) + if err != nil { + t.Fatalf("Result file missing for %s: %s", dir.Name(), err.Error()) + } + + ast, err := Parse(df) + if err != nil { + t.Fatalf("Error parsing %s's dockerfile: %s", dir.Name(), err.Error()) + } + + content, err := ioutil.ReadAll(rf) + if err != nil { + t.Fatalf("Error reading %s's result file: %s", dir.Name(), err.Error()) + } + + if ast.Dump()+"\n" != string(content) { + fmt.Fprintln(os.Stderr, "Result:\n"+ast.Dump()) + fmt.Fprintln(os.Stderr, "Expected:\n"+string(content)) + t.Fatalf("%s: AST dump of dockerfile does not match result", dir.Name()) + } + + df.Close() + rf.Close() + } +} diff --git a/builder/parser/testfiles-negative/env_equals_env/Dockerfile b/builder/parser/testfiles-negative/env_equals_env/Dockerfile new file mode 100644 index 0000000000..08675148ae --- /dev/null +++ b/builder/parser/testfiles-negative/env_equals_env/Dockerfile @@ -0,0 +1,3 @@ +FROM busybox + +ENV PATH=PATH diff --git a/builder/parser/testfiles-negative/html-page-yes-really-thanks-lk4d4/Dockerfile b/builder/parser/testfiles-negative/html-page-yes-really-thanks-lk4d4/Dockerfile new file mode 100644 index 0000000000..90531a4b3e --- /dev/null +++ b/builder/parser/testfiles-negative/html-page-yes-really-thanks-lk4d4/Dockerfile @@ -0,0 +1,2 @@ + + diff --git a/builder/parser/testfiles-negative/shykes-nested-json/Dockerfile b/builder/parser/testfiles-negative/shykes-nested-json/Dockerfile new file mode 100644 index 0000000000..d1be4596c7 --- /dev/null +++ b/builder/parser/testfiles-negative/shykes-nested-json/Dockerfile @@ -0,0 +1 @@ +CMD [ "echo", [ "nested json" ] ] diff --git a/builder/parser/testfiles/brimstone-consuldock/Dockerfile b/builder/parser/testfiles/brimstone-consuldock/Dockerfile new file mode 100644 index 0000000000..5c75a2e0ca --- /dev/null +++ b/builder/parser/testfiles/brimstone-consuldock/Dockerfile @@ -0,0 +1,25 @@ +FROM brimstone/ubuntu:14.04 + +MAINTAINER brimstone@the.narro.ws + +# TORUN -v /var/run/docker.sock:/var/run/docker.sock + +ENV GOPATH /go + +# Set our command +ENTRYPOINT ["/usr/local/bin/consuldock"] + +# Install the packages we need, clean up after them and us +RUN apt-get update \ + && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean \ + && apt-get install -y --no-install-recommends git golang ca-certificates \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists \ + + && go get -v github.com/brimstone/consuldock \ + && mv $GOPATH/bin/consuldock /usr/local/bin/consuldock \ + + && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \ + && apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \ + && rm /tmp/dpkg.* \ + && rm -rf $GOPATH diff --git a/builder/parser/testfiles/brimstone-consuldock/result b/builder/parser/testfiles/brimstone-consuldock/result new file mode 100644 index 0000000000..cc8fab2131 --- /dev/null +++ b/builder/parser/testfiles/brimstone-consuldock/result @@ -0,0 +1,5 @@ +(from "brimstone/ubuntu:14.04") +(maintainer "brimstone@the.narro.ws") +(env "GOPATH" "/go") +(entrypoint "/usr/local/bin/consuldock") +(run "apt-get update && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean && apt-get install -y --no-install-recommends git golang ca-certificates && apt-get clean && rm -rf /var/lib/apt/lists && go get -v github.com/brimstone/consuldock && mv $GOPATH/bin/consuldock /usr/local/bin/consuldock && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty && apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') && rm /tmp/dpkg.* && rm -rf $GOPATH") diff --git a/builder/parser/testfiles/brimstone-docker-consul/Dockerfile b/builder/parser/testfiles/brimstone-docker-consul/Dockerfile new file mode 100644 index 0000000000..25ae352166 --- /dev/null +++ b/builder/parser/testfiles/brimstone-docker-consul/Dockerfile @@ -0,0 +1,52 @@ +FROM brimstone/ubuntu:14.04 + +CMD [] + +ENTRYPOINT ["/usr/bin/consul", "agent", "-server", "-data-dir=/consul", "-client=0.0.0.0", "-ui-dir=/webui"] + +EXPOSE 8500 8600 8400 8301 8302 + +RUN apt-get update \ + && apt-get install -y unzip wget \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists + +RUN cd /tmp \ + && wget https://dl.bintray.com/mitchellh/consul/0.3.1_web_ui.zip \ + -O web_ui.zip \ + && unzip web_ui.zip \ + && mv dist /webui \ + && rm web_ui.zip + +RUN apt-get update \ + && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean \ + && apt-get install -y --no-install-recommends unzip wget \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists \ + + && cd /tmp \ + && wget https://dl.bintray.com/mitchellh/consul/0.3.1_web_ui.zip \ + -O web_ui.zip \ + && unzip web_ui.zip \ + && mv dist /webui \ + && rm web_ui.zip \ + + && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \ + && apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \ + && rm /tmp/dpkg.* + +ENV GOPATH /go + +RUN apt-get update \ + && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean \ + && apt-get install -y --no-install-recommends git golang ca-certificates build-essential \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists \ + + && go get -v github.com/hashicorp/consul \ + && mv $GOPATH/bin/consul /usr/bin/consul \ + + && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty \ + && apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') \ + && rm /tmp/dpkg.* \ + && rm -rf $GOPATH diff --git a/builder/parser/testfiles/brimstone-docker-consul/result b/builder/parser/testfiles/brimstone-docker-consul/result new file mode 100644 index 0000000000..8c989e6211 --- /dev/null +++ b/builder/parser/testfiles/brimstone-docker-consul/result @@ -0,0 +1,9 @@ +(from "brimstone/ubuntu:14.04") +(cmd) +(entrypoint "/usr/bin/consul" "agent" "-server" "-data-dir=/consul" "-client=0.0.0.0" "-ui-dir=/webui") +(expose "8500" "8600" "8400" "8301" "8302") +(run "apt-get update && apt-get install -y unzip wget && apt-get clean && rm -rf /var/lib/apt/lists") +(run "cd /tmp && wget https://dl.bintray.com/mitchellh/consul/0.3.1_web_ui.zip -O web_ui.zip && unzip web_ui.zip && mv dist /webui && rm web_ui.zip") +(run "apt-get update && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean && apt-get install -y --no-install-recommends unzip wget && apt-get clean && rm -rf /var/lib/apt/lists && cd /tmp && wget https://dl.bintray.com/mitchellh/consul/0.3.1_web_ui.zip -O web_ui.zip && unzip web_ui.zip && mv dist /webui && rm web_ui.zip && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty && apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') && rm /tmp/dpkg.*") +(env "GOPATH" "/go") +(run "apt-get update && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.clean && apt-get install -y --no-install-recommends git golang ca-certificates build-essential && apt-get clean && rm -rf /var/lib/apt/lists && go get -v github.com/hashicorp/consul && mv $GOPATH/bin/consul /usr/bin/consul && dpkg -l | awk '/^ii/ {print $2}' > /tmp/dpkg.dirty && apt-get remove --purge -y $(diff /tmp/dpkg.clean /tmp/dpkg.dirty | awk '/^>/ {print $2}') && rm /tmp/dpkg.* && rm -rf $GOPATH") diff --git a/builder/parser/testfiles/continueIndent/Dockerfile b/builder/parser/testfiles/continueIndent/Dockerfile new file mode 100644 index 0000000000..42b324e77b --- /dev/null +++ b/builder/parser/testfiles/continueIndent/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:14.04 + +RUN echo hello\ + world\ + goodnight \ + moon\ + light\ +ning +RUN echo hello \ + world +RUN echo hello \ +world +RUN echo hello \ +goodbye\ +frog +RUN echo hello \ +world +RUN echo hi \ + \ + world \ +\ + good\ +\ +night +RUN echo goodbye\ +frog +RUN echo good\ +bye\ +frog + +RUN echo hello \ +# this is a comment + +# this is a comment with a blank line surrounding it + +this is some more useful stuff diff --git a/builder/parser/testfiles/continueIndent/result b/builder/parser/testfiles/continueIndent/result new file mode 100644 index 0000000000..268ae073c8 --- /dev/null +++ b/builder/parser/testfiles/continueIndent/result @@ -0,0 +1,10 @@ +(from "ubuntu:14.04") +(run "echo hello world goodnight moon lightning") +(run "echo hello world") +(run "echo hello world") +(run "echo hello goodbyefrog") +(run "echo hello world") +(run "echo hi world goodnight") +(run "echo goodbyefrog") +(run "echo goodbyefrog") +(run "echo hello this is some more useful stuff") diff --git a/builder/parser/testfiles/cpuguy83-nagios/Dockerfile b/builder/parser/testfiles/cpuguy83-nagios/Dockerfile new file mode 100644 index 0000000000..8ccb71a578 --- /dev/null +++ b/builder/parser/testfiles/cpuguy83-nagios/Dockerfile @@ -0,0 +1,54 @@ +FROM cpuguy83/ubuntu +ENV NAGIOS_HOME /opt/nagios +ENV NAGIOS_USER nagios +ENV NAGIOS_GROUP nagios +ENV NAGIOS_CMDUSER nagios +ENV NAGIOS_CMDGROUP nagios +ENV NAGIOSADMIN_USER nagiosadmin +ENV NAGIOSADMIN_PASS nagios +ENV APACHE_RUN_USER nagios +ENV APACHE_RUN_GROUP nagios +ENV NAGIOS_TIMEZONE UTC + +RUN sed -i 's/universe/universe multiverse/' /etc/apt/sources.list +RUN apt-get update && apt-get install -y iputils-ping netcat build-essential snmp snmpd snmp-mibs-downloader php5-cli apache2 libapache2-mod-php5 runit bc postfix bsd-mailx +RUN ( egrep -i "^${NAGIOS_GROUP}" /etc/group || groupadd $NAGIOS_GROUP ) && ( egrep -i "^${NAGIOS_CMDGROUP}" /etc/group || groupadd $NAGIOS_CMDGROUP ) +RUN ( id -u $NAGIOS_USER || useradd --system $NAGIOS_USER -g $NAGIOS_GROUP -d $NAGIOS_HOME ) && ( id -u $NAGIOS_CMDUSER || useradd --system -d $NAGIOS_HOME -g $NAGIOS_CMDGROUP $NAGIOS_CMDUSER ) + +ADD http://downloads.sourceforge.net/project/nagios/nagios-3.x/nagios-3.5.1/nagios-3.5.1.tar.gz?r=http%3A%2F%2Fwww.nagios.org%2Fdownload%2Fcore%2Fthanks%2F%3Ft%3D1398863696&ts=1398863718&use_mirror=superb-dca3 /tmp/nagios.tar.gz +RUN cd /tmp && tar -zxvf nagios.tar.gz && cd nagios && ./configure --prefix=${NAGIOS_HOME} --exec-prefix=${NAGIOS_HOME} --enable-event-broker --with-nagios-command-user=${NAGIOS_CMDUSER} --with-command-group=${NAGIOS_CMDGROUP} --with-nagios-user=${NAGIOS_USER} --with-nagios-group=${NAGIOS_GROUP} && make all && make install && make install-config && make install-commandmode && cp sample-config/httpd.conf /etc/apache2/conf.d/nagios.conf +ADD http://www.nagios-plugins.org/download/nagios-plugins-1.5.tar.gz /tmp/ +RUN cd /tmp && tar -zxvf nagios-plugins-1.5.tar.gz && cd nagios-plugins-1.5 && ./configure --prefix=${NAGIOS_HOME} && make && make install + +RUN sed -i.bak 's/.*\=www\-data//g' /etc/apache2/envvars +RUN export DOC_ROOT="DocumentRoot $(echo $NAGIOS_HOME/share)"; sed -i "s,DocumentRoot.*,$DOC_ROOT," /etc/apache2/sites-enabled/000-default + +RUN ln -s ${NAGIOS_HOME}/bin/nagios /usr/local/bin/nagios && mkdir -p /usr/share/snmp/mibs && chmod 0755 /usr/share/snmp/mibs && touch /usr/share/snmp/mibs/.foo + +RUN echo "use_timezone=$NAGIOS_TIMEZONE" >> ${NAGIOS_HOME}/etc/nagios.cfg && echo "SetEnv TZ \"${NAGIOS_TIMEZONE}\"" >> /etc/apache2/conf.d/nagios.conf + +RUN mkdir -p ${NAGIOS_HOME}/etc/conf.d && mkdir -p ${NAGIOS_HOME}/etc/monitor && ln -s /usr/share/snmp/mibs ${NAGIOS_HOME}/libexec/mibs +RUN echo "cfg_dir=${NAGIOS_HOME}/etc/conf.d" >> ${NAGIOS_HOME}/etc/nagios.cfg +RUN echo "cfg_dir=${NAGIOS_HOME}/etc/monitor" >> ${NAGIOS_HOME}/etc/nagios.cfg +RUN download-mibs && echo "mibs +ALL" > /etc/snmp/snmp.conf + +RUN sed -i 's,/bin/mail,/usr/bin/mail,' /opt/nagios/etc/objects/commands.cfg && \ + sed -i 's,/usr/usr,/usr,' /opt/nagios/etc/objects/commands.cfg +RUN cp /etc/services /var/spool/postfix/etc/ + +RUN mkdir -p /etc/sv/nagios && mkdir -p /etc/sv/apache && rm -rf /etc/sv/getty-5 && mkdir -p /etc/sv/postfix +ADD nagios.init /etc/sv/nagios/run +ADD apache.init /etc/sv/apache/run +ADD postfix.init /etc/sv/postfix/run +ADD postfix.stop /etc/sv/postfix/finish + +ADD start.sh /usr/local/bin/start_nagios + +ENV APACHE_LOCK_DIR /var/run +ENV APACHE_LOG_DIR /var/log/apache2 + +EXPOSE 80 + +VOLUME ["/opt/nagios/var", "/opt/nagios/etc", "/opt/nagios/libexec", "/var/log/apache2", "/usr/share/snmp/mibs"] + +CMD ["/usr/local/bin/start_nagios"] diff --git a/builder/parser/testfiles/cpuguy83-nagios/result b/builder/parser/testfiles/cpuguy83-nagios/result new file mode 100644 index 0000000000..25dd3ddfe5 --- /dev/null +++ b/builder/parser/testfiles/cpuguy83-nagios/result @@ -0,0 +1,40 @@ +(from "cpuguy83/ubuntu") +(env "NAGIOS_HOME" "/opt/nagios") +(env "NAGIOS_USER" "nagios") +(env "NAGIOS_GROUP" "nagios") +(env "NAGIOS_CMDUSER" "nagios") +(env "NAGIOS_CMDGROUP" "nagios") +(env "NAGIOSADMIN_USER" "nagiosadmin") +(env "NAGIOSADMIN_PASS" "nagios") +(env "APACHE_RUN_USER" "nagios") +(env "APACHE_RUN_GROUP" "nagios") +(env "NAGIOS_TIMEZONE" "UTC") +(run "sed -i 's/universe/universe multiverse/' /etc/apt/sources.list") +(run "apt-get update && apt-get install -y iputils-ping netcat build-essential snmp snmpd snmp-mibs-downloader php5-cli apache2 libapache2-mod-php5 runit bc postfix bsd-mailx") +(run "( egrep -i \"^${NAGIOS_GROUP}\" /etc/group || groupadd $NAGIOS_GROUP ) && ( egrep -i \"^${NAGIOS_CMDGROUP}\" /etc/group || groupadd $NAGIOS_CMDGROUP )") +(run "( id -u $NAGIOS_USER || useradd --system $NAGIOS_USER -g $NAGIOS_GROUP -d $NAGIOS_HOME ) && ( id -u $NAGIOS_CMDUSER || useradd --system -d $NAGIOS_HOME -g $NAGIOS_CMDGROUP $NAGIOS_CMDUSER )") +(add "http://downloads.sourceforge.net/project/nagios/nagios-3.x/nagios-3.5.1/nagios-3.5.1.tar.gz?r=http%3A%2F%2Fwww.nagios.org%2Fdownload%2Fcore%2Fthanks%2F%3Ft%3D1398863696&ts=1398863718&use_mirror=superb-dca3" "/tmp/nagios.tar.gz") +(run "cd /tmp && tar -zxvf nagios.tar.gz && cd nagios && ./configure --prefix=${NAGIOS_HOME} --exec-prefix=${NAGIOS_HOME} --enable-event-broker --with-nagios-command-user=${NAGIOS_CMDUSER} --with-command-group=${NAGIOS_CMDGROUP} --with-nagios-user=${NAGIOS_USER} --with-nagios-group=${NAGIOS_GROUP} && make all && make install && make install-config && make install-commandmode && cp sample-config/httpd.conf /etc/apache2/conf.d/nagios.conf") +(add "http://www.nagios-plugins.org/download/nagios-plugins-1.5.tar.gz" "/tmp/") +(run "cd /tmp && tar -zxvf nagios-plugins-1.5.tar.gz && cd nagios-plugins-1.5 && ./configure --prefix=${NAGIOS_HOME} && make && make install") +(run "sed -i.bak 's/.*\\=www\\-data//g' /etc/apache2/envvars") +(run "export DOC_ROOT=\"DocumentRoot $(echo $NAGIOS_HOME/share)\"; sed -i \"s,DocumentRoot.*,$DOC_ROOT,\" /etc/apache2/sites-enabled/000-default") +(run "ln -s ${NAGIOS_HOME}/bin/nagios /usr/local/bin/nagios && mkdir -p /usr/share/snmp/mibs && chmod 0755 /usr/share/snmp/mibs && touch /usr/share/snmp/mibs/.foo") +(run "echo \"use_timezone=$NAGIOS_TIMEZONE\" >> ${NAGIOS_HOME}/etc/nagios.cfg && echo \"SetEnv TZ \\\"${NAGIOS_TIMEZONE}\\\"\" >> /etc/apache2/conf.d/nagios.conf") +(run "mkdir -p ${NAGIOS_HOME}/etc/conf.d && mkdir -p ${NAGIOS_HOME}/etc/monitor && ln -s /usr/share/snmp/mibs ${NAGIOS_HOME}/libexec/mibs") +(run "echo \"cfg_dir=${NAGIOS_HOME}/etc/conf.d\" >> ${NAGIOS_HOME}/etc/nagios.cfg") +(run "echo \"cfg_dir=${NAGIOS_HOME}/etc/monitor\" >> ${NAGIOS_HOME}/etc/nagios.cfg") +(run "download-mibs && echo \"mibs +ALL\" > /etc/snmp/snmp.conf") +(run "sed -i 's,/bin/mail,/usr/bin/mail,' /opt/nagios/etc/objects/commands.cfg && sed -i 's,/usr/usr,/usr,' /opt/nagios/etc/objects/commands.cfg") +(run "cp /etc/services /var/spool/postfix/etc/") +(run "mkdir -p /etc/sv/nagios && mkdir -p /etc/sv/apache && rm -rf /etc/sv/getty-5 && mkdir -p /etc/sv/postfix") +(add "nagios.init" "/etc/sv/nagios/run") +(add "apache.init" "/etc/sv/apache/run") +(add "postfix.init" "/etc/sv/postfix/run") +(add "postfix.stop" "/etc/sv/postfix/finish") +(add "start.sh" "/usr/local/bin/start_nagios") +(env "APACHE_LOCK_DIR" "/var/run") +(env "APACHE_LOG_DIR" "/var/log/apache2") +(expose "80") +(volume "/opt/nagios/var" "/opt/nagios/etc" "/opt/nagios/libexec" "/var/log/apache2" "/usr/share/snmp/mibs") +(cmd "/usr/local/bin/start_nagios") diff --git a/builder/parser/testfiles/docker/Dockerfile b/builder/parser/testfiles/docker/Dockerfile new file mode 100644 index 0000000000..68f8f0b78b --- /dev/null +++ b/builder/parser/testfiles/docker/Dockerfile @@ -0,0 +1,105 @@ +# This file describes the standard way to build Docker, using docker +# +# Usage: +# +# # Assemble the full dev environment. This is slow the first time. +# docker build -t docker . +# +# # Mount your source in an interactive container for quick testing: +# docker run -v `pwd`:/go/src/github.com/docker/docker --privileged -i -t docker bash +# +# # Run the test suite: +# docker run --privileged docker hack/make.sh test +# +# # Publish a release: +# docker run --privileged \ +# -e AWS_S3_BUCKET=baz \ +# -e AWS_ACCESS_KEY=foo \ +# -e AWS_SECRET_KEY=bar \ +# -e GPG_PASSPHRASE=gloubiboulga \ +# docker hack/release.sh +# +# Note: Apparmor used to mess with privileged mode, but this is no longer +# the case. Therefore, you don't have to disable it anymore. +# + +docker-version 0.6.1 +FROM ubuntu:14.04 +MAINTAINER Tianon Gravi (@tianon) + +# Packaged dependencies +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \ + apt-utils \ + aufs-tools \ + automake \ + btrfs-tools \ + build-essential \ + curl \ + dpkg-sig \ + git \ + iptables \ + libapparmor-dev \ + libcap-dev \ + libsqlite3-dev \ + lxc=1.0* \ + mercurial \ + pandoc \ + parallel \ + reprepro \ + ruby1.9.1 \ + ruby1.9.1-dev \ + s3cmd=1.1.0* \ + --no-install-recommends + +# Get lvm2 source for compiling statically +RUN git clone --no-checkout https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout -q v2_02_103 +# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags +# note: we don't use "git clone -b" above because it then spews big nasty warnings about 'detached HEAD' state that we can't silence as easily as we can silence them using "git checkout" directly + +# Compile and install lvm2 +RUN cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper && make install_device-mapper +# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL + +# Install Go +RUN curl -sSL https://golang.org/dl/go1.3.src.tar.gz | tar -v -C /usr/local -xz +ENV PATH /usr/local/go/bin:$PATH +ENV GOPATH /go:/go/src/github.com/docker/docker/vendor +RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1 + +# Compile Go for cross compilation +ENV DOCKER_CROSSPLATFORMS \ + linux/386 linux/arm \ + darwin/amd64 darwin/386 \ + freebsd/amd64 freebsd/386 freebsd/arm +# (set an explicit GOARM of 5 for maximum compatibility) +ENV GOARM 5 +RUN cd /usr/local/go/src && bash -xc 'for platform in $DOCKER_CROSSPLATFORMS; do GOOS=${platform%/*} GOARCH=${platform##*/} ./make.bash --no-clean 2>&1; done' + +# Grab Go's cover tool for dead-simple code coverage testing +RUN go get code.google.com/p/go.tools/cmd/cover + +# TODO replace FPM with some very minimal debhelper stuff +RUN gem install --no-rdoc --no-ri fpm --version 1.0.2 + +# Get the "busybox" image source so we can build locally instead of pulling +RUN git clone -b buildroot-2014.02 https://github.com/jpetazzo/docker-busybox.git /docker-busybox + +# Setup s3cmd config +RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg + +# Set user.email so crosbymichael's in-container merge commits go smoothly +RUN git config --global user.email 'docker-dummy@example.com' + +# Add an unprivileged user to be used for tests which need it +RUN groupadd -r docker +RUN useradd --create-home --gid docker unprivilegeduser + +VOLUME /var/lib/docker +WORKDIR /go/src/github.com/docker/docker +ENV DOCKER_BUILDTAGS apparmor selinux + +# Wrap all commands in the "docker-in-docker" script to allow nested containers +ENTRYPOINT ["hack/dind"] + +# Upload docker source +COPY . /go/src/github.com/docker/docker diff --git a/builder/parser/testfiles/docker/result b/builder/parser/testfiles/docker/result new file mode 100644 index 0000000000..915e2ef17b --- /dev/null +++ b/builder/parser/testfiles/docker/result @@ -0,0 +1,25 @@ +(docker-version) +(from "ubuntu:14.04") +(maintainer "Tianon Gravi (@tianon)") +(run "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq apt-utils aufs-tools automake btrfs-tools build-essential curl dpkg-sig git iptables libapparmor-dev libcap-dev libsqlite3-dev lxc=1.0* mercurial pandoc parallel reprepro ruby1.9.1 ruby1.9.1-dev s3cmd=1.1.0* --no-install-recommends") +(run "git clone --no-checkout https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout -q v2_02_103") +(run "cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper && make install_device-mapper") +(run "curl -sSL https://golang.org/dl/go1.3.src.tar.gz | tar -v -C /usr/local -xz") +(env "PATH" "/usr/local/go/bin:$PATH") +(env "GOPATH" "/go:/go/src/github.com/docker/docker/vendor") +(run "cd /usr/local/go/src && ./make.bash --no-clean 2>&1") +(env "DOCKER_CROSSPLATFORMS" "linux/386 linux/arm darwin/amd64 darwin/386 freebsd/amd64 freebsd/386 freebsd/arm") +(env "GOARM" "5") +(run "cd /usr/local/go/src && bash -xc 'for platform in $DOCKER_CROSSPLATFORMS; do GOOS=${platform%/*} GOARCH=${platform##*/} ./make.bash --no-clean 2>&1; done'") +(run "go get code.google.com/p/go.tools/cmd/cover") +(run "gem install --no-rdoc --no-ri fpm --version 1.0.2") +(run "git clone -b buildroot-2014.02 https://github.com/jpetazzo/docker-busybox.git /docker-busybox") +(run "/bin/echo -e '[default]\\naccess_key=$AWS_ACCESS_KEY\\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg") +(run "git config --global user.email 'docker-dummy@example.com'") +(run "groupadd -r docker") +(run "useradd --create-home --gid docker unprivilegeduser") +(volume "/var/lib/docker") +(workdir "/go/src/github.com/docker/docker") +(env "DOCKER_BUILDTAGS" "apparmor selinux") +(entrypoint "hack/dind") +(copy "." "/go/src/github.com/docker/docker") diff --git a/builder/parser/testfiles/escapes/Dockerfile b/builder/parser/testfiles/escapes/Dockerfile new file mode 100644 index 0000000000..1ffb17ef08 --- /dev/null +++ b/builder/parser/testfiles/escapes/Dockerfile @@ -0,0 +1,14 @@ +FROM ubuntu:14.04 +MAINTAINER Erik \\Hollensbe \" + +RUN apt-get \update && \ + apt-get \"install znc -y +ADD \conf\\" /.znc + +RUN foo \ + +bar \ + +baz + +CMD [ "\/usr\\\"/bin/znc", "-f", "-r" ] diff --git a/builder/parser/testfiles/escapes/result b/builder/parser/testfiles/escapes/result new file mode 100644 index 0000000000..13e409cb1a --- /dev/null +++ b/builder/parser/testfiles/escapes/result @@ -0,0 +1,6 @@ +(from "ubuntu:14.04") +(maintainer "Erik \\\\Hollensbe \\\"") +(run "apt-get \\update && apt-get \\\"install znc -y") +(add "\\conf\\\\\"" "/.znc") +(run "foo bar baz") +(cmd "/usr\\\"/bin/znc" "-f" "-r") diff --git a/builder/parser/testfiles/influxdb/Dockerfile b/builder/parser/testfiles/influxdb/Dockerfile new file mode 100644 index 0000000000..587fb9b54b --- /dev/null +++ b/builder/parser/testfiles/influxdb/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:14.04 + +RUN apt-get update && apt-get install wget -y +RUN wget http://s3.amazonaws.com/influxdb/influxdb_latest_amd64.deb +RUN dpkg -i influxdb_latest_amd64.deb +RUN rm -r /opt/influxdb/shared + +VOLUME /opt/influxdb/shared + +CMD /usr/bin/influxdb --pidfile /var/run/influxdb.pid -config /opt/influxdb/shared/config.toml + +EXPOSE 8083 +EXPOSE 8086 +EXPOSE 8090 +EXPOSE 8099 diff --git a/builder/parser/testfiles/influxdb/result b/builder/parser/testfiles/influxdb/result new file mode 100644 index 0000000000..0998e87e63 --- /dev/null +++ b/builder/parser/testfiles/influxdb/result @@ -0,0 +1,11 @@ +(from "ubuntu:14.04") +(run "apt-get update && apt-get install wget -y") +(run "wget http://s3.amazonaws.com/influxdb/influxdb_latest_amd64.deb") +(run "dpkg -i influxdb_latest_amd64.deb") +(run "rm -r /opt/influxdb/shared") +(volume "/opt/influxdb/shared") +(cmd "/usr/bin/influxdb --pidfile /var/run/influxdb.pid -config /opt/influxdb/shared/config.toml") +(expose "8083") +(expose "8086") +(expose "8090") +(expose "8099") diff --git a/builder/parser/testfiles/jeztah-invalid-json-json-inside-string-double/Dockerfile b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string-double/Dockerfile new file mode 100644 index 0000000000..39fe27d99c --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string-double/Dockerfile @@ -0,0 +1 @@ +CMD "[\"echo\", \"Phew, I just managed to escaped those double quotes\"]" diff --git a/builder/parser/testfiles/jeztah-invalid-json-json-inside-string-double/result b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string-double/result new file mode 100644 index 0000000000..afc220c2a7 --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string-double/result @@ -0,0 +1 @@ +(cmd "\"[\\\"echo\\\", \\\"Phew, I just managed to escaped those double quotes\\\"]\"") diff --git a/builder/parser/testfiles/jeztah-invalid-json-json-inside-string/Dockerfile b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string/Dockerfile new file mode 100644 index 0000000000..eaae081a06 --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string/Dockerfile @@ -0,0 +1 @@ +CMD '["echo", "Well, JSON in a string is JSON too?"]' diff --git a/builder/parser/testfiles/jeztah-invalid-json-json-inside-string/result b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string/result new file mode 100644 index 0000000000..484804e2b2 --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-json-inside-string/result @@ -0,0 +1 @@ +(cmd "'[\"echo\", \"Well, JSON in a string is JSON too?\"]'") diff --git a/builder/parser/testfiles/jeztah-invalid-json-single-quotes/Dockerfile b/builder/parser/testfiles/jeztah-invalid-json-single-quotes/Dockerfile new file mode 100644 index 0000000000..c3ac63c07a --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-single-quotes/Dockerfile @@ -0,0 +1 @@ +CMD ['echo','single quotes are invalid JSON'] diff --git a/builder/parser/testfiles/jeztah-invalid-json-single-quotes/result b/builder/parser/testfiles/jeztah-invalid-json-single-quotes/result new file mode 100644 index 0000000000..6147891207 --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-single-quotes/result @@ -0,0 +1 @@ +(cmd "['echo','single quotes are invalid JSON']") diff --git a/builder/parser/testfiles/jeztah-invalid-json-unterminated-bracket/Dockerfile b/builder/parser/testfiles/jeztah-invalid-json-unterminated-bracket/Dockerfile new file mode 100644 index 0000000000..5fd4afa522 --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-unterminated-bracket/Dockerfile @@ -0,0 +1 @@ +CMD ["echo", "Please, close the brackets when you're done" diff --git a/builder/parser/testfiles/jeztah-invalid-json-unterminated-bracket/result b/builder/parser/testfiles/jeztah-invalid-json-unterminated-bracket/result new file mode 100644 index 0000000000..1ffbb8ff85 --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-unterminated-bracket/result @@ -0,0 +1 @@ +(cmd "[\"echo\", \"Please, close the brackets when you're done\"") diff --git a/builder/parser/testfiles/jeztah-invalid-json-unterminated-string/Dockerfile b/builder/parser/testfiles/jeztah-invalid-json-unterminated-string/Dockerfile new file mode 100644 index 0000000000..30cc4bb48f --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-unterminated-string/Dockerfile @@ -0,0 +1 @@ +CMD ["echo", "look ma, no quote!] diff --git a/builder/parser/testfiles/jeztah-invalid-json-unterminated-string/result b/builder/parser/testfiles/jeztah-invalid-json-unterminated-string/result new file mode 100644 index 0000000000..32048147b5 --- /dev/null +++ b/builder/parser/testfiles/jeztah-invalid-json-unterminated-string/result @@ -0,0 +1 @@ +(cmd "[\"echo\", \"look ma, no quote!]") diff --git a/builder/parser/testfiles/kartar-entrypoint-oddities/Dockerfile b/builder/parser/testfiles/kartar-entrypoint-oddities/Dockerfile new file mode 100644 index 0000000000..35f9c24aa6 --- /dev/null +++ b/builder/parser/testfiles/kartar-entrypoint-oddities/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:14.04 +MAINTAINER James Turnbull "james@example.com" +ENV REFRESHED_AT 2014-06-01 +RUN apt-get update +RUN apt-get -y install redis-server redis-tools +EXPOSE 6379 +ENTRYPOINT [ "/usr/bin/redis-server" ] diff --git a/builder/parser/testfiles/kartar-entrypoint-oddities/result b/builder/parser/testfiles/kartar-entrypoint-oddities/result new file mode 100644 index 0000000000..b5ac6fe445 --- /dev/null +++ b/builder/parser/testfiles/kartar-entrypoint-oddities/result @@ -0,0 +1,7 @@ +(from "ubuntu:14.04") +(maintainer "James Turnbull \"james@example.com\"") +(env "REFRESHED_AT" "2014-06-01") +(run "apt-get update") +(run "apt-get -y install redis-server redis-tools") +(expose "6379") +(entrypoint "/usr/bin/redis-server") diff --git a/builder/parser/testfiles/lk4d4-the-edge-case-generator/Dockerfile b/builder/parser/testfiles/lk4d4-the-edge-case-generator/Dockerfile new file mode 100644 index 0000000000..188395fe83 --- /dev/null +++ b/builder/parser/testfiles/lk4d4-the-edge-case-generator/Dockerfile @@ -0,0 +1,48 @@ +FROM busybox:buildroot-2014.02 + +MAINTAINER docker + +ONBUILD RUN ["echo", "test"] +ONBUILD RUN echo test +ONBUILD COPY . / + + +# RUN Commands \ +# linebreak in comment \ +RUN ["ls", "-la"] +RUN ["echo", "'1234'"] +RUN echo "1234" +RUN echo 1234 +RUN echo '1234' && \ + echo "456" && \ + echo 789 +RUN sh -c 'echo root:testpass \ + > /tmp/passwd' +RUN mkdir -p /test /test2 /test3/test + +# ENV \ +ENV SCUBA 1 DUBA 3 +ENV SCUBA "1 DUBA 3" + +# CMD \ +CMD ["echo", "test"] +CMD echo test +CMD echo "test" +CMD echo 'test' +CMD echo 'test' | wc - + +#EXPOSE\ +EXPOSE 3000 +EXPOSE 9000 5000 6000 + +USER docker +USER docker:root + +VOLUME ["/test"] +VOLUME ["/test", "/test2"] +VOLUME /test3 + +WORKDIR /test + +ADD . / +COPY . copy diff --git a/builder/parser/testfiles/lk4d4-the-edge-case-generator/result b/builder/parser/testfiles/lk4d4-the-edge-case-generator/result new file mode 100644 index 0000000000..6f7d57a396 --- /dev/null +++ b/builder/parser/testfiles/lk4d4-the-edge-case-generator/result @@ -0,0 +1,29 @@ +(from "busybox:buildroot-2014.02") +(maintainer "docker ") +(onbuild (run "echo" "test")) +(onbuild (run "echo test")) +(onbuild (copy "." "/")) +(run "ls" "-la") +(run "echo" "'1234'") +(run "echo \"1234\"") +(run "echo 1234") +(run "echo '1234' && echo \"456\" && echo 789") +(run "sh -c 'echo root:testpass > /tmp/passwd'") +(run "mkdir -p /test /test2 /test3/test") +(env "SCUBA" "1 DUBA 3") +(env "SCUBA" "\"1 DUBA 3\"") +(cmd "echo" "test") +(cmd "echo test") +(cmd "echo \"test\"") +(cmd "echo 'test'") +(cmd "echo 'test' | wc -") +(expose "3000") +(expose "9000" "5000" "6000") +(user "docker") +(user "docker:root") +(volume "/test") +(volume "/test" "/test2") +(volume "/test3") +(workdir "/test") +(add "." "/") +(copy "." "copy") diff --git a/builder/parser/testfiles/mail/Dockerfile b/builder/parser/testfiles/mail/Dockerfile new file mode 100644 index 0000000000..f64c1168c1 --- /dev/null +++ b/builder/parser/testfiles/mail/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:14.04 + +RUN apt-get update -qy && apt-get install mutt offlineimap vim-nox abook elinks curl tmux cron zsh -y +ADD .muttrc / +ADD .offlineimaprc / +ADD .tmux.conf / +ADD mutt /.mutt +ADD vim /.vim +ADD vimrc /.vimrc +ADD crontab /etc/crontab +RUN chmod 644 /etc/crontab +RUN mkdir /Mail +RUN mkdir /.offlineimap +RUN echo "export TERM=screen-256color" >/.zshenv + +CMD setsid cron; tmux -2 diff --git a/builder/parser/testfiles/mail/result b/builder/parser/testfiles/mail/result new file mode 100644 index 0000000000..a0efcf04b6 --- /dev/null +++ b/builder/parser/testfiles/mail/result @@ -0,0 +1,14 @@ +(from "ubuntu:14.04") +(run "apt-get update -qy && apt-get install mutt offlineimap vim-nox abook elinks curl tmux cron zsh -y") +(add ".muttrc" "/") +(add ".offlineimaprc" "/") +(add ".tmux.conf" "/") +(add "mutt" "/.mutt") +(add "vim" "/.vim") +(add "vimrc" "/.vimrc") +(add "crontab" "/etc/crontab") +(run "chmod 644 /etc/crontab") +(run "mkdir /Mail") +(run "mkdir /.offlineimap") +(run "echo \"export TERM=screen-256color\" >/.zshenv") +(cmd "setsid cron; tmux -2") diff --git a/builder/parser/testfiles/multiple-volumes/Dockerfile b/builder/parser/testfiles/multiple-volumes/Dockerfile new file mode 100644 index 0000000000..57bb5976a3 --- /dev/null +++ b/builder/parser/testfiles/multiple-volumes/Dockerfile @@ -0,0 +1,3 @@ +FROM foo + +VOLUME /opt/nagios/var /opt/nagios/etc /opt/nagios/libexec /var/log/apache2 /usr/share/snmp/mibs diff --git a/builder/parser/testfiles/multiple-volumes/result b/builder/parser/testfiles/multiple-volumes/result new file mode 100644 index 0000000000..18dbdeeaa0 --- /dev/null +++ b/builder/parser/testfiles/multiple-volumes/result @@ -0,0 +1,2 @@ +(from "foo") +(volume "/opt/nagios/var" "/opt/nagios/etc" "/opt/nagios/libexec" "/var/log/apache2" "/usr/share/snmp/mibs") diff --git a/builder/parser/testfiles/mumble/Dockerfile b/builder/parser/testfiles/mumble/Dockerfile new file mode 100644 index 0000000000..5b9ec06a6c --- /dev/null +++ b/builder/parser/testfiles/mumble/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:14.04 + +RUN apt-get update && apt-get install libcap2-bin mumble-server -y + +ADD ./mumble-server.ini /etc/mumble-server.ini + +CMD /usr/sbin/murmurd diff --git a/builder/parser/testfiles/mumble/result b/builder/parser/testfiles/mumble/result new file mode 100644 index 0000000000..a0036a943e --- /dev/null +++ b/builder/parser/testfiles/mumble/result @@ -0,0 +1,4 @@ +(from "ubuntu:14.04") +(run "apt-get update && apt-get install libcap2-bin mumble-server -y") +(add "./mumble-server.ini" "/etc/mumble-server.ini") +(cmd "/usr/sbin/murmurd") diff --git a/builder/parser/testfiles/nginx/Dockerfile b/builder/parser/testfiles/nginx/Dockerfile new file mode 100644 index 0000000000..bf8368e1ca --- /dev/null +++ b/builder/parser/testfiles/nginx/Dockerfile @@ -0,0 +1,14 @@ +FROM ubuntu:14.04 +MAINTAINER Erik Hollensbe + +RUN apt-get update && apt-get install nginx-full -y +RUN rm -rf /etc/nginx +ADD etc /etc/nginx +RUN chown -R root:root /etc/nginx +RUN /usr/sbin/nginx -qt +RUN mkdir /www + +CMD ["/usr/sbin/nginx"] + +VOLUME /www +EXPOSE 80 diff --git a/builder/parser/testfiles/nginx/result b/builder/parser/testfiles/nginx/result new file mode 100644 index 0000000000..56ddb6f258 --- /dev/null +++ b/builder/parser/testfiles/nginx/result @@ -0,0 +1,11 @@ +(from "ubuntu:14.04") +(maintainer "Erik Hollensbe ") +(run "apt-get update && apt-get install nginx-full -y") +(run "rm -rf /etc/nginx") +(add "etc" "/etc/nginx") +(run "chown -R root:root /etc/nginx") +(run "/usr/sbin/nginx -qt") +(run "mkdir /www") +(cmd "/usr/sbin/nginx") +(volume "/www") +(expose "80") diff --git a/builder/parser/testfiles/tf2/Dockerfile b/builder/parser/testfiles/tf2/Dockerfile new file mode 100644 index 0000000000..72b79bdd7d --- /dev/null +++ b/builder/parser/testfiles/tf2/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:12.04 + +EXPOSE 27015 +EXPOSE 27005 +EXPOSE 26901 +EXPOSE 27020 + +RUN apt-get update && apt-get install libc6-dev-i386 curl unzip -y +RUN mkdir -p /steam +RUN curl http://media.steampowered.com/client/steamcmd_linux.tar.gz | tar vxz -C /steam +ADD ./script /steam/script +RUN /steam/steamcmd.sh +runscript /steam/script +RUN curl http://mirror.pointysoftware.net/alliedmodders/mmsource-1.10.0-linux.tar.gz | tar vxz -C /steam/tf2/tf +RUN curl http://mirror.pointysoftware.net/alliedmodders/sourcemod-1.5.3-linux.tar.gz | tar vxz -C /steam/tf2/tf +ADD ./server.cfg /steam/tf2/tf/cfg/server.cfg +ADD ./ctf_2fort.cfg /steam/tf2/tf/cfg/ctf_2fort.cfg +ADD ./sourcemod.cfg /steam/tf2/tf/cfg/sourcemod/sourcemod.cfg +RUN rm -r /steam/tf2/tf/addons/sourcemod/configs +ADD ./configs /steam/tf2/tf/addons/sourcemod/configs +RUN mkdir -p /steam/tf2/tf/addons/sourcemod/translations/en +RUN cp /steam/tf2/tf/addons/sourcemod/translations/*.txt /steam/tf2/tf/addons/sourcemod/translations/en + +CMD cd /steam/tf2 && ./srcds_run -port 27015 +ip 0.0.0.0 +map ctf_2fort -autoupdate -steam_dir /steam -steamcmd_script /steam/script +tf_bot_quota 12 +tf_bot_quota_mode fill diff --git a/builder/parser/testfiles/tf2/result b/builder/parser/testfiles/tf2/result new file mode 100644 index 0000000000..d4f94cd8be --- /dev/null +++ b/builder/parser/testfiles/tf2/result @@ -0,0 +1,20 @@ +(from "ubuntu:12.04") +(expose "27015") +(expose "27005") +(expose "26901") +(expose "27020") +(run "apt-get update && apt-get install libc6-dev-i386 curl unzip -y") +(run "mkdir -p /steam") +(run "curl http://media.steampowered.com/client/steamcmd_linux.tar.gz | tar vxz -C /steam") +(add "./script" "/steam/script") +(run "/steam/steamcmd.sh +runscript /steam/script") +(run "curl http://mirror.pointysoftware.net/alliedmodders/mmsource-1.10.0-linux.tar.gz | tar vxz -C /steam/tf2/tf") +(run "curl http://mirror.pointysoftware.net/alliedmodders/sourcemod-1.5.3-linux.tar.gz | tar vxz -C /steam/tf2/tf") +(add "./server.cfg" "/steam/tf2/tf/cfg/server.cfg") +(add "./ctf_2fort.cfg" "/steam/tf2/tf/cfg/ctf_2fort.cfg") +(add "./sourcemod.cfg" "/steam/tf2/tf/cfg/sourcemod/sourcemod.cfg") +(run "rm -r /steam/tf2/tf/addons/sourcemod/configs") +(add "./configs" "/steam/tf2/tf/addons/sourcemod/configs") +(run "mkdir -p /steam/tf2/tf/addons/sourcemod/translations/en") +(run "cp /steam/tf2/tf/addons/sourcemod/translations/*.txt /steam/tf2/tf/addons/sourcemod/translations/en") +(cmd "cd /steam/tf2 && ./srcds_run -port 27015 +ip 0.0.0.0 +map ctf_2fort -autoupdate -steam_dir /steam -steamcmd_script /steam/script +tf_bot_quota 12 +tf_bot_quota_mode fill") diff --git a/builder/parser/testfiles/weechat/Dockerfile b/builder/parser/testfiles/weechat/Dockerfile new file mode 100644 index 0000000000..4842088166 --- /dev/null +++ b/builder/parser/testfiles/weechat/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:14.04 + +RUN apt-get update -qy && apt-get install tmux zsh weechat-curses -y + +ADD .weechat /.weechat +ADD .tmux.conf / +RUN echo "export TERM=screen-256color" >/.zshenv + +CMD zsh -c weechat diff --git a/builder/parser/testfiles/weechat/result b/builder/parser/testfiles/weechat/result new file mode 100644 index 0000000000..c3abb4c54f --- /dev/null +++ b/builder/parser/testfiles/weechat/result @@ -0,0 +1,6 @@ +(from "ubuntu:14.04") +(run "apt-get update -qy && apt-get install tmux zsh weechat-curses -y") +(add ".weechat" "/.weechat") +(add ".tmux.conf" "/") +(run "echo \"export TERM=screen-256color\" >/.zshenv") +(cmd "zsh -c weechat") diff --git a/builder/parser/testfiles/znc/Dockerfile b/builder/parser/testfiles/znc/Dockerfile new file mode 100644 index 0000000000..3a4da6e916 --- /dev/null +++ b/builder/parser/testfiles/znc/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:14.04 +MAINTAINER Erik Hollensbe + +RUN apt-get update && apt-get install znc -y +ADD conf /.znc + +CMD [ "/usr/bin/znc", "-f", "-r" ] diff --git a/builder/parser/testfiles/znc/result b/builder/parser/testfiles/znc/result new file mode 100644 index 0000000000..5493b255fd --- /dev/null +++ b/builder/parser/testfiles/znc/result @@ -0,0 +1,5 @@ +(from "ubuntu:14.04") +(maintainer "Erik Hollensbe ") +(run "apt-get update && apt-get install znc -y") +(add "conf" "/.znc") +(cmd "/usr/bin/znc" "-f" "-r") diff --git a/builder/parser/utils.go b/builder/parser/utils.go new file mode 100644 index 0000000000..096c4e31e9 --- /dev/null +++ b/builder/parser/utils.go @@ -0,0 +1,94 @@ +package parser + +import ( + "fmt" + "strings" +) + +// QuoteString walks characters (after trimming), escapes any quotes and +// escapes, then wraps the whole thing in quotes. Very useful for generating +// argument output in nodes. +func QuoteString(str string) string { + result := "" + chars := strings.Split(strings.TrimSpace(str), "") + + for _, char := range chars { + switch char { + case `"`: + result += `\"` + case `\`: + result += `\\` + default: + result += char + } + } + + return `"` + result + `"` +} + +// dumps the AST defined by `node` as a list of sexps. Returns a string +// suitable for printing. +func (node *Node) Dump() string { + str := "" + str += node.Value + + for _, n := range node.Children { + str += "(" + n.Dump() + ")\n" + } + + if node.Next != nil { + for n := node.Next; n != nil; n = n.Next { + if len(n.Children) > 0 { + str += " " + n.Dump() + } else { + str += " " + QuoteString(n.Value) + } + } + } + + return strings.TrimSpace(str) +} + +// performs the dispatch based on the two primal strings, cmd and args. Please +// look at the dispatch table in parser.go to see how these dispatchers work. +func fullDispatch(cmd, args string) (*Node, map[string]bool, error) { + fn := dispatch[cmd] + + // Ignore invalid Dockerfile instructions + if fn == nil { + fn = parseIgnore + } + + sexp, attrs, err := fn(args) + if err != nil { + return nil, nil, err + } + + return sexp, attrs, nil +} + +// splitCommand takes a single line of text and parses out the cmd and args, +// which are used for dispatching to more exact parsing functions. +func splitCommand(line string) (string, string, error) { + cmdline := TOKEN_WHITESPACE.Split(line, 2) + + if len(cmdline) != 2 { + return "", "", fmt.Errorf("We do not understand this file. Please ensure it is a valid Dockerfile. Parser error at %q", line) + } + + cmd := strings.ToLower(cmdline[0]) + // the cmd should never have whitespace, but it's possible for the args to + // have trailing whitespace. + return cmd, strings.TrimSpace(cmdline[1]), nil +} + +// covers comments and empty lines. Lines should be trimmed before passing to +// this function. +func stripComments(line string) string { + // string is already trimmed at this point + if TOKEN_COMMENT.MatchString(line) { + return TOKEN_COMMENT.ReplaceAllString(line, "") + } + + return line +} diff --git a/builder/support.go b/builder/support.go new file mode 100644 index 0000000000..a084190f2c --- /dev/null +++ b/builder/support.go @@ -0,0 +1,46 @@ +package builder + +import ( + "regexp" + "strings" +) + +var ( + // `\\\\+|[^\\]|\b|\A` - match any number of "\\" (ie, properly-escaped backslashes), or a single non-backslash character, or a word boundary, or beginning-of-line + // `\$` - match literal $ + // `[[:alnum:]_]+` - match things like `$SOME_VAR` + // `{[[:alnum:]_]+}` - match things like `${SOME_VAR}` + tokenEnvInterpolation = regexp.MustCompile(`(\\\\+|[^\\]|\b|\A)\$([[:alnum:]_]+|{[[:alnum:]_]+})`) + // this intentionally punts on more exotic interpolations like ${SOME_VAR%suffix} and lets the shell handle those directly +) + +// handle environment replacement. Used in dispatcher. +func (b *Builder) replaceEnv(str string) string { + for _, match := range tokenEnvInterpolation.FindAllString(str, -1) { + match = match[strings.Index(match, "$"):] + matchKey := strings.Trim(match, "${}") + + for _, keyval := range b.Config.Env { + tmp := strings.SplitN(keyval, "=", 2) + if tmp[0] == matchKey { + str = strings.Replace(str, match, tmp[1], -1) + break + } + } + } + + return str +} + +func handleJsonArgs(args []string, attributes map[string]bool) []string { + if len(args) == 0 { + return []string{} + } + + if attributes != nil && attributes["json"] { + return args + } + + // literal string command, not an exec array + return []string{strings.Join(args, " ")} +} diff --git a/builtins/builtins.go b/builtins/builtins.go index 0aa2f43c16..f952d728b2 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -61,7 +61,7 @@ func dockerVersion(job *engine.Job) engine.Status { v := &engine.Env{} v.SetJson("Version", dockerversion.VERSION) v.SetJson("ApiVersion", api.APIVERSION) - v.Set("GitCommit", dockerversion.GITCOMMIT) + v.SetJson("GitCommit", dockerversion.GITCOMMIT) v.Set("GoVersion", runtime.Version()) v.Set("Os", runtime.GOOS) v.Set("Arch", runtime.GOARCH) diff --git a/contrib/check-config.sh b/contrib/check-config.sh index cb6a4f2b50..afaabbc956 100755 --- a/contrib/check-config.sh +++ b/contrib/check-config.sh @@ -146,13 +146,14 @@ echo 'Optional Features:' flags=( MEMCG_SWAP RESOURCE_COUNTERS + CGROUP_PERF ) check_flags "${flags[@]}" echo '- Storage Drivers:' { echo '- "'$(wrap_color 'aufs' blue)'":' - check_flags AUFS_FS | sed 's/^/ /' + check_flags AUFS_FS EXT4_FS_POSIX_ACL EXT4_FS_SECURITY | sed 's/^/ /' if ! is_set AUFS_FS && grep -q aufs /proc/filesystems; then echo " $(wrap_color '(note that some kernels include AUFS patches but not the AUFS_FS flag)' bold black)" fi @@ -161,7 +162,7 @@ echo '- Storage Drivers:' check_flags BTRFS_FS | sed 's/^/ /' echo '- "'$(wrap_color 'devicemapper' blue)'":' - check_flags BLK_DEV_DM DM_THIN_PROVISIONING EXT4_FS | sed 's/^/ /' + check_flags BLK_DEV_DM DM_THIN_PROVISIONING EXT4_FS EXT4_FS_POSIX_ACL EXT4_FS_SECURITY | sed 's/^/ /' } | sed 's/^/ /' echo diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index d6d622ff33..cc16d4825f 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -25,63 +25,59 @@ __docker_q() { docker 2>/dev/null "$@" } -__docker_containers_all() -{ - local containers="$( __docker_q ps -a -q )" - local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" - COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) +__docker_containers_all() { + local IFS=$'\n' + local containers=( $(__docker_q ps -aq --no-trunc) ) + if [ "$1" ]; then + containers=( $(__docker_q inspect --format "{{if $1}}{{.Id}}{{end}}" "${containers[@]}") ) + fi + local names=( $(__docker_q inspect --format '{{.Name}}' "${containers[@]}") ) + names=( "${names[@]#/}" ) # trim off the leading "/" from the container names + unset IFS + COMPREPLY=( $(compgen -W "${names[*]} ${containers[*]}" -- "$cur") ) } -__docker_containers_running() -{ - local containers="$( __docker_q ps -q )" - local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" - COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) +__docker_containers_running() { + __docker_containers_all '.State.Running' } -__docker_containers_stopped() -{ - local containers="$( { __docker_q ps -a -q; __docker_q ps -q; } | sort | uniq -u )" - local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" - COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) +__docker_containers_stopped() { + __docker_containers_all 'not .State.Running' } -__docker_image_repos() -{ - local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^$' )" - COMPREPLY=( $( compgen -W "$repos" -- "$cur" ) ) +__docker_containers_pauseable() { + __docker_containers_all 'and .State.Running (not .State.Paused)' } -__docker_image_repos_and_tags() -{ - local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^$' )" - local images="$( __docker_q images | awk 'NR>1{print $1":"$2}' | grep -v '^:' )" - COMPREPLY=( $( compgen -W "$repos $images" -- "$cur" ) ) +__docker_containers_unpauseable() { + __docker_containers_all '.State.Paused' +} + +__docker_image_repos() { + local repos="$(__docker_q images | awk 'NR>1 && $1 != "" { print $1 }')" + COMPREPLY=( $(compgen -W "$repos" -- "$cur") ) +} + +__docker_image_repos_and_tags() { + local reposAndTags="$(__docker_q images | awk 'NR>1 && $1 != "" { print $1; print $1":"$2 }')" + COMPREPLY=( $(compgen -W "$reposAndTags" -- "$cur") ) __ltrim_colon_completions "$cur" } -__docker_image_repos_and_tags_and_ids() -{ - local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^$' )" - local images="$( __docker_q images | awk 'NR>1{print $1":"$2}' | grep -v '^:' )" - local ids="$( __docker_q images -a -q )" - COMPREPLY=( $( compgen -W "$repos $images $ids" -- "$cur" ) ) +__docker_image_repos_and_tags_and_ids() { + local images="$(__docker_q images -a --no-trunc | awk 'NR>1 { print $3; if ($1 != "") { print $1; print $1":"$2 } }')" + COMPREPLY=( $(compgen -W "$images" -- "$cur") ) __ltrim_colon_completions "$cur" } -__docker_containers_and_images() -{ - local containers="$( __docker_q ps -a -q )" - local names="$( __docker_q inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" - local repos="$( __docker_q images | awk 'NR>1{print $1}' | grep -v '^$' )" - local images="$( __docker_q images | awk 'NR>1{print $1":"$2}' | grep -v '^:' )" - local ids="$( __docker_q images -a -q )" - COMPREPLY=( $( compgen -W "$containers $names $repos $images $ids" -- "$cur" ) ) - __ltrim_colon_completions "$cur" +__docker_containers_and_images() { + __docker_containers_all + local containers=( "${COMPREPLY[@]}" ) + __docker_image_repos_and_tags_and_ids + COMPREPLY+=( "${containers[@]}" ) } -__docker_pos_first_nonflag() -{ +__docker_pos_first_nonflag() { local argument_flags=$1 local counter=$cpos @@ -103,8 +99,7 @@ __docker_pos_first_nonflag() echo $counter } -_docker_docker() -{ +_docker_docker() { case "$prev" in -H) return @@ -118,13 +113,12 @@ _docker_docker() COMPREPLY=( $( compgen -W "-H" -- "$cur" ) ) ;; *) - COMPREPLY=( $( compgen -W "$commands help" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) ) ;; esac } -_docker_attach() -{ +_docker_attach() { case "$cur" in -*) COMPREPLY=( $( compgen -W "--no-stdin --sig-proxy" -- "$cur" ) ) @@ -138,8 +132,7 @@ _docker_attach() esac } -_docker_build() -{ +_docker_build() { case "$prev" in -t|--tag) __docker_image_repos_and_tags @@ -151,7 +144,7 @@ _docker_build() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-t --tag -q --quiet --no-cache --rm" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-t --tag -q --quiet --no-cache --rm --force-rm" -- "$cur" ) ) ;; *) local counter="$(__docker_pos_first_nonflag '-t|--tag')" @@ -162,8 +155,7 @@ _docker_build() esac } -_docker_commit() -{ +_docker_commit() { case "$prev" in -m|--message|-a|--author|--run) return @@ -193,8 +185,7 @@ _docker_commit() esac } -_docker_cp() -{ +_docker_cp() { local counter=$(__docker_pos_first_nonflag) if [ $cword -eq $counter ]; then case "$cur" in @@ -217,273 +208,7 @@ _docker_cp() fi } -_docker_diff() -{ - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - __docker_containers_all - fi -} - -_docker_events() -{ - case "$prev" in - --since) - return - ;; - *) - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--since" -- "$cur" ) ) - ;; - *) - ;; - esac -} - -_docker_export() -{ - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - __docker_containers_all - fi -} - -_docker_help() -{ - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - COMPREPLY=( $( compgen -W "$commands" -- "$cur" ) ) - fi -} - -_docker_history() -{ - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-q --quiet --no-trunc" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - __docker_image_repos_and_tags_and_ids - fi - ;; - esac -} - -_docker_images() -{ - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-q --quiet -a --all --no-trunc -v --viz -t --tree" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - __docker_image_repos - fi - ;; - esac -} - -_docker_import() -{ - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - return - fi - (( counter++ )) - - if [ $cword -eq $counter ]; then - __docker_image_repos_and_tags - return - fi -} - -_docker_info() -{ - return -} - -_docker_inspect() -{ - case "$prev" in - -f|--format) - return - ;; - *) - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-f --format" -- "$cur" ) ) - ;; - *) - __docker_containers_and_images - ;; - esac -} - -_docker_kill() -{ - __docker_containers_running -} - -_docker_load() -{ - return -} - -_docker_login() -{ - case "$prev" in - -u|--username|-p|--password|-e|--email) - return - ;; - *) - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-u --username -p --password -e --email" -- "$cur" ) ) - ;; - *) - ;; - esac -} - -_docker_logs() -{ - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-f --follow" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - __docker_containers_all - fi - ;; - esac -} - -_docker_port() -{ - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - __docker_containers_all - fi -} - -_docker_ps() -{ - case "$prev" in - --since|--before) - __docker_containers_all - ;; - -n) - return - ;; - *) - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-q --quiet -s --size -a --all --no-trunc -l --latest --since --before -n" -- "$cur" ) ) - ;; - *) - ;; - esac -} - -_docker_pull() -{ - case "$prev" in - -t|--tag) - return - ;; - *) - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-t --tag" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag '-t|--tag') - if [ $cword -eq $counter ]; then - __docker_image_repos_and_tags - fi - ;; - esac -} - -_docker_push() -{ - local counter=$(__docker_pos_first_nonflag) - if [ $cword -eq $counter ]; then - __docker_image_repos_and_tags - fi -} - -_docker_restart() -{ - case "$prev" in - -t|--time) - return - ;; - *) - ;; - esac - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-t --time" -- "$cur" ) ) - ;; - *) - __docker_containers_all - ;; - esac -} - -_docker_rm() -{ - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-f --force -l --link -v --volumes" -- "$cur" ) ) - return - ;; - *) - local force= - for arg in "${COMP_WORDS[@]}"; do - case "$arg" in - -f|--force) - __docker_containers_all - return - ;; - esac - done - __docker_containers_stopped - return - ;; - esac -} - -_docker_rmi() -{ - __docker_image_repos_and_tags_and_ids -} - -_docker_run() -{ +_docker_create() { case "$prev" in -a|--attach) COMPREPLY=( $( compgen -W 'stdin stdout stderr' -- "$cur" ) ) @@ -539,7 +264,7 @@ _docker_run() case "$cur" in -*) - COMPREPLY=( $( compgen -W "--rm -d --detach -n --networking --privileged -P --publish-all -i --interactive -t --tty --cidfile --entrypoint -h --hostname -m --memory -u --user -w --workdir -c --cpu-shares --sig-proxy --name -a --attach -v --volume --link -e --env -p --publish --expose --dns --volumes-from --lxc-conf" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-n --networking --privileged -P --publish-all -i --interactive -t --tty --cidfile --entrypoint -h --hostname -m --memory -u --user -w --workdir -c --cpu-shares --name -a --attach -v --volume --link -e --env -p --publish --expose --dns --volumes-from --lxc-conf" -- "$cur" ) ) ;; *) local counter=$(__docker_pos_first_nonflag '--cidfile|--volumes-from|-v|--volume|-e|--env|--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf') @@ -551,16 +276,346 @@ _docker_run() esac } -_docker_save() -{ +_docker_diff() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_containers_all + fi +} + +_docker_events() { + case "$prev" in + --since) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--since" -- "$cur" ) ) + ;; + *) + ;; + esac +} + +_docker_exec() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-d --detach -i --interactive -t --tty" -- "$cur" ) ) + ;; + *) + __docker_containers_running + ;; + esac +} + +_docker_export() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_containers_all + fi +} + +_docker_help() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) ) + fi +} + +_docker_history() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-q --quiet --no-trunc" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_image_repos_and_tags_and_ids + fi + ;; + esac +} + +_docker_images() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-q --quiet -a --all --no-trunc -v --viz -t --tree" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_image_repos + fi + ;; + esac +} + +_docker_import() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + return + fi + (( counter++ )) + + if [ $cword -eq $counter ]; then + __docker_image_repos_and_tags + return + fi +} + +_docker_info() { + return +} + +_docker_inspect() { + case "$prev" in + -f|--format) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-f --format" -- "$cur" ) ) + ;; + *) + __docker_containers_and_images + ;; + esac +} + +_docker_kill() { + __docker_containers_running +} + +_docker_load() { + return +} + +_docker_login() { + case "$prev" in + -u|--username|-p|--password|-e|--email) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-u --username -p --password -e --email" -- "$cur" ) ) + ;; + *) + ;; + esac +} + +_docker_logs() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-f --follow" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_containers_all + fi + ;; + esac +} + +_docker_pause() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_containers_pauseable + fi +} + +_docker_port() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_containers_all + fi +} + +_docker_ps() { + case "$prev" in + --since|--before) + __docker_containers_all + ;; + -n) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-q --quiet -s --size -a --all --no-trunc -l --latest --since --before -n" -- "$cur" ) ) + ;; + *) + ;; + esac +} + +_docker_pull() { + case "$prev" in + -t|--tag) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-t --tag" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag '-t|--tag') + if [ $cword -eq $counter ]; then + __docker_image_repos_and_tags + fi + ;; + esac +} + +_docker_push() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_image_repos_and_tags + fi +} + +_docker_restart() { + case "$prev" in + -t|--time) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-t --time" -- "$cur" ) ) + ;; + *) + __docker_containers_all + ;; + esac +} + +_docker_rm() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-f --force -l --link -v --volumes" -- "$cur" ) ) + return + ;; + *) + local force= + for arg in "${COMP_WORDS[@]}"; do + case "$arg" in + -f|--force) + __docker_containers_all + return + ;; + esac + done + __docker_containers_stopped + return + ;; + esac +} + +_docker_rmi() { + __docker_image_repos_and_tags_and_ids +} + +_docker_run() { + case "$prev" in + -a|--attach) + COMPREPLY=( $( compgen -W 'stdin stdout stderr' -- "$cur" ) ) + return + ;; + --cidfile|--env-file) + _filedir + return + ;; + --volumes-from) + __docker_containers_all + return + ;; + -v|--volume) + case "$cur" in + *:*) + # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine) + ;; + '') + COMPREPLY=( $( compgen -W '/' -- "$cur" ) ) + compopt -o nospace + ;; + /*) + _filedir + compopt -o nospace + ;; + esac + return + ;; + -e|--env) + COMPREPLY=( $( compgen -e -- "$cur" ) ) + compopt -o nospace + return + ;; + --link) + case "$cur" in + *:*) + ;; + *) + __docker_containers_running + COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) ) + compopt -o nospace + ;; + esac + return + ;; + --entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|--cpuset|-c|--cpu-shares|-n|--name|-p|--publish|--expose|--dns|--lxc-conf) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--rm -d --detach -n --networking --privileged -P --publish-all -i --interactive -t --tty --cidfile --entrypoint -h --hostname -m --memory -u --user -w --workdir --cpuset -c --cpu-shares --sig-proxy --name -a --attach -v --volume --link -e --env -p --publish --expose --dns --volumes-from --lxc-conf --security-opt" -- "$cur" ) ) + ;; + *) + + local counter=$(__docker_pos_first_nonflag '--cidfile|--volumes-from|-v|--volume|-e|--env|--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|--cpuset|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf|--security-opt') + + if [ $cword -eq $counter ]; then + __docker_image_repos_and_tags_and_ids + fi + ;; + esac +} + +_docker_save() { local counter=$(__docker_pos_first_nonflag) if [ $cword -eq $counter ]; then __docker_image_repos_and_tags_and_ids fi } -_docker_search() -{ +_docker_search() { case "$prev" in -s|--stars) return @@ -578,8 +633,7 @@ _docker_search() esac } -_docker_start() -{ +_docker_start() { case "$cur" in -*) COMPREPLY=( $( compgen -W "-a --attach -i --interactive" -- "$cur" ) ) @@ -590,8 +644,7 @@ _docker_start() esac } -_docker_stop() -{ +_docker_stop() { case "$prev" in -t|--time) return @@ -610,8 +663,7 @@ _docker_stop() esac } -_docker_tag() -{ +_docker_tag() { case "$cur" in -*) COMPREPLY=( $( compgen -W "-f --force" -- "$cur" ) ) @@ -633,61 +685,68 @@ _docker_tag() esac } -_docker_top() -{ +_docker_unpause() { + local counter=$(__docker_pos_first_nonflag) + if [ $cword -eq $counter ]; then + __docker_containers_unpauseable + fi +} + +_docker_top() { local counter=$(__docker_pos_first_nonflag) if [ $cword -eq $counter ]; then __docker_containers_running fi } -_docker_version() -{ +_docker_version() { return } -_docker_wait() -{ +_docker_wait() { __docker_containers_all } -_docker() -{ - local commands=" - attach - build - commit - cp - diff - events - export - history - images - import - info - insert - inspect - kill - load - login - logs - port - ps - pull - push - restart - rm - rmi - run - save - search - start - stop - tag - top - version - wait - " +_docker() { + local commands=( + attach + build + commit + cp + create + diff + events + exec + export + history + images + import + info + insert + inspect + kill + load + login + logs + pause + port + ps + pull + push + restart + rm + rmi + run + save + search + start + stop + tag + top + unpause + version + wait + ) COMPREPLY=() local cur prev words cword diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index ba83526c75..48b0279cee 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -16,7 +16,7 @@ function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand' for i in (commandline -opc) - if contains -- $i attach build commit cp diff events export history images import info insert inspect kill load login logs port ps pull push restart rm rmi run save search start stop tag top version wait + if contains -- $i attach build commit cp create diff events export history images import info insert inspect kill load login logs port ps pull push restart rm rmi run save search start stop tag top version wait return 1 end end @@ -72,6 +72,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -a '(__fish_pri # build complete -c docker -f -n '__fish_docker_no_subcommand' -a build -d 'Build an image from a Dockerfile' +complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l force-rm -d 'Always remove intermediate containers, even after unsuccessful builds' complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l no-cache -d 'Do not use cache when building the image' complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s q -l quiet -d 'Suppress the verbose output generated by the containers' complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l rm -d 'Remove intermediate containers after a successful build' @@ -87,6 +88,33 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -a '(__fish_pri # cp complete -c docker -f -n '__fish_docker_no_subcommand' -a cp -d "Copy files/folders from a container's filesystem to the host path" +# create +complete -c docker -f -n '__fish_docker_no_subcommand' -a run -d 'Run a command in a new container' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to the host interfaces' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s a -l attach -d 'Attach to stdin, stdout or stderr.' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s c -l cpu-shares -d 'CPU shares (relative weight)' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cidfile -d 'Write the container ID to the file' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l dns -d 'Set custom dns servers' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s e -l env -d 'Set environment variables' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l entrypoint -d 'Overwrite the default entrypoint of the image' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l expose -d 'Expose a port from the container without publishing it to your host' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s h -l hostname -d 'Container host name' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s i -l interactive -d 'Keep stdin open even if not attached' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container (name:alias)' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d 'Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: , where unit = b, k, m or g)' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s n -l networking -d 'Enable networking for this container' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s p -l publish -d "Publish a container's port to the host (format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort) (use 'docker port' to see the actual mapping)" +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l privileged -d 'Give extended privileges to this container' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-tty' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container' +complete -c docker -A -f -n '__fish_seen_subcommand_from run' -a '(__fish_print_docker_images)' -d "Image" + + # diff complete -c docker -f -n '__fish_docker_no_subcommand' -a diff -d "Inspect changes on a container's filesystem" complete -c docker -A -f -n '__fish_seen_subcommand_from diff' -a '(__fish_print_docker_containers all)' -d "Container" diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index faf17b2bea..aff59ee77c 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -5,7 +5,7 @@ # version: 0.3.0 # github: https://github.com/felixr/docker-zsh-completion # -# contributers: +# contributors: # - Felix Riedel # - Vincent Bernat # @@ -37,65 +37,86 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -__parse_docker_list() { - awk ' -NR == 1 { - idx=1;i=0;f[i]=0 - header=$0 - while ( match(header, / ([A-Z]+|[A-Z]+ [A-Z]+)/) ) { - idx += RSTART+1 - f[++i]=idx - header = substr($0,idx) - } - f[++i]=999 -} +__docker_get_containers() { + local kind expl + declare -a running stopped lines args -NR > 1 '"$1"' { - for(j=0;j 1 && $1 != ""){printf("%s", $1);if ($2 != "") printf("\\:%s", $2); printf("\n")}')"}) - images=($images ${(f)"$(_call_program commands docker images | awk '(NR > 1){printf("%s:%-15s in %s\n", $3,$2,$1)}')"}) - _describe -t docker-images "Images" images + images=(${${${${(f)"$(_call_program commands docker images)"}[2,-1]}/ ##/\\:}%% *}) + images=(${${images%\\:}#} ${${${(f)"$(_call_program commands docker images)"}[2,-1]}/(#b)([^ ]##) ##([^ ]##) ##([^ ]##)*/${match[3]}:${(r:15:: :::)match[2]} in ${match[1]}}) + _describe -t docker-images "images" images } __docker_tags() { local expl declare -a tags - tags=(${(f)"$(_call_program commands docker images | awk '(NR>1){print $2}'| sort | uniq)"}) + tags=(${${${${${(f)"$(_call_program commands docker images)"}#* }## #}%% *}[2,-1]}) _describe -t docker-tags "tags" tags } @@ -124,16 +145,15 @@ __docker_search() { if ( [[ ${(P)+cachename} -eq 0 ]] || _cache_invalid ${cachename#_} ) \ && ! _retrieve_cache ${cachename#_}; then _message "Searching for ${searchterm}..." - result=(${(f)"$(_call_program commands docker search ${searchterm} | awk '(NR>2){print $1}')"}) + result=(${${${(f)"$(_call_program commands docker search ${searchterm})"}%% *}[2,-1]}) _store_cache ${cachename#_} result fi - _wanted dockersearch expl 'Available images' compadd -a result + _wanted dockersearch expl 'available images' compadd -a result } __docker_caching_policy() { - # oldp=( "$1"(Nmh+24) ) # 24 hour - oldp=( "$1"(Nmh+1) ) # 24 hour + oldp=( "$1"(Nmh+1) ) # 1 hour (( $#oldp )) } @@ -141,8 +161,8 @@ __docker_caching_policy() __docker_repositories () { local expl declare -a repos - repos=(${(f)"$(_call_program commands docker images | sed -e '1d' -e 's/[ ].*//' | sort | uniq)"}) - _describe -t docker-repos "Repositories" repos "$@" + repos=(${${${(f)"$(_call_program commands docker images)"}%% *}[2,-1]}) + _describe -t docker-repos "repositories" repos "$@" } __docker_commands () { @@ -157,8 +177,7 @@ __docker_commands () { if ( [[ ${+_docker_subcommands} -eq 0 ]] || _cache_invalid docker_subcommands) \ && ! _retrieve_cache docker_subcommands; then - _docker_subcommands=(${${(f)"$(_call_program commands - docker 2>&1 | sed -e '1,6d' -e '/^[ ]*$/d' -e 's/[ ]*\([^ ]\+\)\s*\([^ ].*\)/\1:\2/' )"}}) + _docker_subcommands=(${${${${(f)"$(_call_program commands docker 2>&1)"}[5,-1]}## #}/ ##/:}) _docker_subcommands=($_docker_subcommands 'help:Show help for a command') _store_cache docker_subcommands _docker_subcommands fi @@ -176,16 +195,17 @@ __docker_subcommand () { ;; (build) _arguments \ + '--force-rm[Always remove intermediate containers, even after unsuccessful builds]' \ '--no-cache[Do not use cache when building the image]' \ '-q[Suppress verbose build output]' \ '--rm[Remove intermediate containers after a successful build]' \ - '-t=-:repository:__docker_repositories_with_tags' \ + '-t:repository:__docker_repositories_with_tags' \ ':path or URL:_directories' ;; (commit) _arguments \ '--author=-[Author]:author: ' \ - '-m=-[Commit message]:message: ' \ + '-m[Commit message]:message: ' \ '--run=-[Configuration automatically applied when the image is run]:configuration: ' \ ':container:__docker_containers' \ ':repository:__docker_repositories_with_tags' @@ -204,9 +224,42 @@ __docker_subcommand () { ;; esac ;; + (create) + _arguments \ + '-P[Publish all exposed ports to the host]' \ + '-a[Attach to stdin, stdout or stderr]' \ + '-c=-[CPU shares (relative weight)]:CPU shares:(0 10 100 200 500 800 1000)' \ + '--cidfile=-[Write the container ID to the file]:CID file:_files' \ + '*--dns=-[Set custom dns servers]:dns server: ' \ + '*-e=-[Set environment variables]:environment variable: ' \ + '--entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \ + '*--expose=-[Expose a port from the container without publishing it]: ' \ + '-h=-[Container host name]:hostname:_hosts' \ + '-i[Keep stdin open even if not attached]' \ + '--link=-[Add link to another container]:link:->link' \ + '--lxc-conf=-[Add custom lxc options]:lxc options: ' \ + '-m=-[Memory limit (in bytes)]:limit: ' \ + '--name=-[Container name]:name: ' \ + '*-p=-[Expose a container'"'"'s port to the host]:port:_ports' \ + '--privileged[Give extended privileges to this container]' \ + '-t[Allocate a pseudo-tty]' \ + '-u=-[Username or UID]:user:_users' \ + '*-v=-[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\ + '--volumes-from=-[Mount volumes from the specified container]:volume: ' \ + '-w=-[Working directory inside the container]:directory:_directories' \ + '(-):images:__docker_images' \ + '(-):command: _command_names -e' \ + '*::arguments: _normal' (diff|export) _arguments '*:containers:__docker_containers' ;; + (exec) + _arguments \ + '-d[Detached mode: leave the container running in the background]' \ + '-i[Keep stdin open even if not attached]' \ + '-t[Allocate a pseudo-tty]' \ + ':containers:__docker_runningcontainers' + ;; (history) _arguments \ '--no-trunc[Do not truncate output]' \ @@ -251,9 +304,9 @@ __docker_subcommand () { ;; (login) _arguments \ - '-e=-[Email]:email: ' \ - '-p=-[Password]:password: ' \ - '-u=-[Username]:username: ' \ + '-e[Email]:email: ' \ + '-p[Password]:password: ' \ + '-u[Username]:username: ' \ ':server: ' ;; (logs) @@ -283,7 +336,7 @@ __docker_subcommand () { '*:images:__docker_images' ;; (restart|stop) - _arguments '-t=-[Number of seconds to try to stop for before killing the container]:seconds to before killing:(1 5 10 30 60)' \ + _arguments '-t[Number of seconds to try to stop for before killing the container]:seconds to before killing:(1 5 10 30 60)' \ '*:containers:__docker_runningcontainers' ;; (top) @@ -302,7 +355,7 @@ __docker_subcommand () { '-a[Show all containers]' \ '--before=-[Show only container created before...]:containers:__docker_containers' \ '-l[Show only the latest created container]' \ - '-n=-[Show n last created containers, include non-running one]:n:(1 5 10 25 50)' \ + '-n[Show n last created containers, include non-running one]:n:(1 5 10 25 50)' \ '--no-trunc[Do not truncate output]' \ '-q[Only show numeric IDs]' \ '-s[Display sizes]' \ @@ -318,28 +371,28 @@ __docker_subcommand () { _arguments \ '-P[Publish all exposed ports to the host]' \ '-a[Attach to stdin, stdout or stderr]' \ - '-c=-[CPU shares (relative weight)]:CPU shares:(0 10 100 200 500 800 1000)' \ + '-c[CPU shares (relative weight)]:CPU shares:(0 10 100 200 500 800 1000)' \ '--cidfile=-[Write the container ID to the file]:CID file:_files' \ '-d[Detached mode: leave the container running in the background]' \ '*--dns=-[Set custom dns servers]:dns server: ' \ - '*-e=-[Set environment variables]:environment variable: ' \ + '*-e[Set environment variables]:environment variable: ' \ '--entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \ '*--expose=-[Expose a port from the container without publishing it]: ' \ - '-h=-[Container host name]:hostname:_hosts' \ + '-h[Container host name]:hostname:_hosts' \ '-i[Keep stdin open even if not attached]' \ '--link=-[Add link to another container]:link:->link' \ '--lxc-conf=-[Add custom lxc options]:lxc options: ' \ - '-m=-[Memory limit (in bytes)]:limit: ' \ + '-m[Memory limit (in bytes)]:limit: ' \ '--name=-[Container name]:name: ' \ - '*-p=-[Expose a container'"'"'s port to the host]:port:_ports' \ + '*-p[Expose a container'"'"'s port to the host]:port:_ports' \ '--privileged[Give extended privileges to this container]' \ '--rm[Remove intermediate containers when it exits]' \ '--sig-proxy[Proxify all received signal]' \ '-t[Allocate a pseudo-tty]' \ - '-u=-[Username or UID]:user:_users' \ - '*-v=-[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\ + '-u[Username or UID]:user:_users' \ + '*-v[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\ '--volumes-from=-[Mount volumes from the specified container]:volume: ' \ - '-w=-[Working directory inside the container]:directory:_directories' \ + '-w[Working directory inside the container]:directory:_directories' \ '(-):images:__docker_images' \ '(-):command: _command_names -e' \ '*::arguments: _normal' @@ -359,7 +412,7 @@ __docker_subcommand () { _arguments ':name:__docker_search' ;; (push) - _arguments ':repository:__docker_repositories_with_tags' + _arguments ':images:__docker_images' ;; (save) _arguments \ @@ -389,7 +442,7 @@ _docker () { typeset -A opt_args _arguments -C \ - '-H=-[tcp://host:port to bind/connect to]:socket: ' \ + '-H[tcp://host:port to bind/connect to]:socket: ' \ '(-): :->command' \ '(-)*:: :->option-or-argument' @@ -408,3 +461,11 @@ _docker () { } _docker "$@" + +# Local Variables: +# mode: Shell-Script +# sh-indentation: 4 +# indent-tabs-mode: nil +# sh-basic-offset: 4 +# End: +# vim: ft=zsh sw=4 ts=4 et diff --git a/contrib/desktop-integration/README.md b/contrib/desktop-integration/README.md index 02181a5f75..85a01b9ee9 100644 --- a/contrib/desktop-integration/README.md +++ b/contrib/desktop-integration/README.md @@ -7,5 +7,5 @@ desktop applications. Examples ======== -* Data container: ./data/Dockerfile creates a data image sharing /data volume -* Iceweasel: ./iceweasel/Dockerfile shows a way to dockerize a common multimedia application +* Chromium: ./chromium/Dockerfile shows a way to dockerize a common application +* Gparted: ./gparted/Dockerfile shows a way to dockerize a common application w devices diff --git a/contrib/desktop-integration/chromium/Dockerfile b/contrib/desktop-integration/chromium/Dockerfile new file mode 100644 index 0000000000..0e0a7ce90e --- /dev/null +++ b/contrib/desktop-integration/chromium/Dockerfile @@ -0,0 +1,38 @@ +# VERSION: 0.1 +# DESCRIPTION: Create chromium container with its dependencies +# AUTHOR: Jessica Frazelle +# COMMENTS: +# This file describes how to build a Chromium container with all +# dependencies installed. It uses native X11 unix socket. +# Tested on Debian Jessie +# USAGE: +# # Download Chromium Dockerfile +# wget http://raw.githubusercontent.com/docker/docker/master/contrib/desktop-integration/chromium/Dockerfile +# +# # Build chromium image +# docker build -t chromium . +# +# # Run stateful data-on-host chromium. For ephemeral, remove -v /data/chromium:/data +# docker run -v /data/chromium:/data -v /tmp/.X11-unix:/tmp/.X11-unix \ +# -e DISPLAY=unix$DISPLAY chromium + +# # To run stateful dockerized data containers +# docker run --volumes-from chromium-data -v /tmp/.X11-unix:/tmp/.X11-unix \ +# -e DISPLAY=unix$DISPLAY chromium + +DOCKER_VERSION 1.3 + +# Base docker image +FROM debian:jessie +MAINTAINER Jessica Frazelle + +# Install Chromium +RUN apt-get update && apt-get install -y \ + chromium \ + chromium-l10n \ + libcanberra-gtk-module \ + libexif-dev \ + --no-install-recommends + +# Autorun chromium +CMD ["/usr/bin/chromium", "--no-sandbox", "--user-data-dir=/data"] diff --git a/contrib/desktop-integration/data/Dockerfile b/contrib/desktop-integration/data/Dockerfile deleted file mode 100644 index 236912f904..0000000000 --- a/contrib/desktop-integration/data/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -# VERSION: 0.1 -# DESCRIPTION: Create data image sharing /data volume -# AUTHOR: Daniel Mizyrycki -# COMMENTS: -# This image is used as base for all data containers. -# /data volume is owned by sysadmin. -# USAGE: -# # Download data Dockerfile -# wget http://raw.githubusercontent.com/dotcloud/docker/master/contrib/desktop-integration/data/Dockerfile -# -# # Build data image -# docker build -t data . -# -# # Create a data container. (eg: iceweasel-data) -# docker run --name iceweasel-data data true -# -# # List data from it -# docker run --volumes-from iceweasel-data busybox ls -al /data - -docker-version 0.6.5 - -# Smallest base image, just to launch a container -FROM busybox -MAINTAINER Daniel Mizyrycki - -# Create a regular user -RUN echo 'sysadmin:x:1000:1000::/data:/bin/sh' >> /etc/passwd -RUN echo 'sysadmin:x:1000:' >> /etc/group - -# Create directory for that user -RUN mkdir /data -RUN chown sysadmin.sysadmin /data - -# Add content to /data. This will keep sysadmin ownership -RUN touch /data/init_volume - -# Create /data volume -VOLUME /data diff --git a/contrib/desktop-integration/gparted/Dockerfile b/contrib/desktop-integration/gparted/Dockerfile new file mode 100644 index 0000000000..6db1d24098 --- /dev/null +++ b/contrib/desktop-integration/gparted/Dockerfile @@ -0,0 +1,33 @@ +# VERSION: 0.1 +# DESCRIPTION: Create gparted container with its dependencies +# AUTHOR: Jessica Frazelle +# COMMENTS: +# This file describes how to build a gparted container with all +# dependencies installed. It uses native X11 unix socket. +# Tested on Debian Jessie +# USAGE: +# # Download gparted Dockerfile +# wget http://raw.githubusercontent.com/docker/docker/master/contrib/desktop-integration/gparted/Dockerfile +# +# # Build gparted image +# docker build -t gparted . +# +# docker run -v /tmp/.X11-unix:/tmp/.X11-unix \ +# --device=/dev/sda:/dev/sda \ +# -e DISPLAY=unix$DISPLAY gparted +# + +DOCKER-VERSION 1.3 + +# Base docker image +FROM debian:jessie +MAINTAINER Jessica Frazelle + +# Install Gparted and its dependencies +RUN apt-get update && apt-get install -y \ + gparted \ + libcanberra-gtk-module \ + --no-install-recommends + +# Autorun gparted +CMD ["/usr/sbin/gparted"] diff --git a/contrib/desktop-integration/iceweasel/Dockerfile b/contrib/desktop-integration/iceweasel/Dockerfile deleted file mode 100644 index 0f3e8f2527..0000000000 --- a/contrib/desktop-integration/iceweasel/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -# VERSION: 0.7 -# DESCRIPTION: Create iceweasel container with its dependencies -# AUTHOR: Daniel Mizyrycki -# COMMENTS: -# This file describes how to build a Iceweasel container with all -# dependencies installed. It uses native X11 unix socket and alsa -# sound devices. Tested on Debian 7.2 -# USAGE: -# # Download Iceweasel Dockerfile -# wget http://raw.githubusercontent.com/dotcloud/docker/master/contrib/desktop-integration/iceweasel/Dockerfile -# -# # Build iceweasel image -# docker build -t iceweasel . -# -# # Run stateful data-on-host iceweasel. For ephemeral, remove -v /data/iceweasel:/data -# docker run -v /data/iceweasel:/data -v /tmp/.X11-unix:/tmp/.X11-unix \ -# -v /dev/snd:/dev/snd --lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \ -# -e DISPLAY=unix$DISPLAY iceweasel -# -# # To run stateful dockerized data containers -# docker run --volumes-from iceweasel-data -v /tmp/.X11-unix:/tmp/.X11-unix \ -# -v /dev/snd:/dev/snd --lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \ -# -e DISPLAY=unix$DISPLAY iceweasel - -docker-version 0.6.5 - -# Base docker image -FROM debian:wheezy -MAINTAINER Daniel Mizyrycki - -# Install Iceweasel and "sudo" -RUN apt-get update && apt-get install -y iceweasel sudo - -# create sysadmin account -RUN useradd -m -d /data -p saIVpsc0EVTwA sysadmin -RUN sed -Ei 's/sudo:x:27:/sudo:x:27:sysadmin/' /etc/group -RUN sed -Ei 's/(\%sudo\s+ALL=\(ALL\:ALL\) )ALL/\1 NOPASSWD:ALL/' /etc/sudoers - -# Autorun iceweasel. -no-remote is necessary to create a new container, as -# iceweasel appears to communicate with itself through X11. -CMD ["/usr/bin/sudo", "-u", "sysadmin", "-H", "-E", "/usr/bin/iceweasel", "-no-remote"] diff --git a/contrib/init/systemd/docker.service b/contrib/init/systemd/docker.service index 0cb31e32c0..83c810d13a 100644 --- a/contrib/init/systemd/docker.service +++ b/contrib/init/systemd/docker.service @@ -10,4 +10,4 @@ LimitNOFILE=1048576 LimitNPROC=1048576 [Install] -Also=docker.socket +WantedBy=multi-user.target diff --git a/contrib/init/systemd/docker.socket b/contrib/init/systemd/docker.socket index 9db5049150..7dd95098e4 100644 --- a/contrib/init/systemd/docker.socket +++ b/contrib/init/systemd/docker.socket @@ -1,5 +1,6 @@ [Unit] Description=Docker Socket for the API +PartOf=docker.service [Socket] ListenStream=/var/run/docker.sock diff --git a/contrib/init/sysvinit-redhat/docker b/contrib/init/sysvinit-redhat/docker index 0c985094e6..eadf02c75c 100755 --- a/contrib/init/sysvinit-redhat/docker +++ b/contrib/init/sysvinit-redhat/docker @@ -68,7 +68,7 @@ start() { stop() { echo -n $"Stopping $prog: " - killproc -p $pidfile $prog + killproc -p $pidfile -d 300 $prog retval=$? echo [ $retval -eq 0 ] && rm -f $lockfile diff --git a/contrib/mkimage.sh b/contrib/mkimage.sh index 803d1627df..cd2fa748d8 100755 --- a/contrib/mkimage.sh +++ b/contrib/mkimage.sh @@ -6,7 +6,7 @@ mkimg="$(basename "$0")" usage() { echo >&2 "usage: $mkimg [-d dir] [-t tag] script [script-args]" echo >&2 " ie: $mkimg -t someuser/debian debootstrap --variant=minbase jessie" - echo >&2 " $mkimg -t someuser/ubuntu debootstrap --include=ubuntu-minimal --components main,universe trusty" + echo >&2 " $mkimg -t someuser/ubuntu debootstrap --include=ubuntu-minimal --components=main,universe trusty" echo >&2 " $mkimg -t someuser/busybox busybox-static" echo >&2 " $mkimg -t someuser/centos:5 rinse --distribution centos-5" echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4" diff --git a/contrib/syntax/kate/Dockerfile.xml b/contrib/syntax/kate/Dockerfile.xml new file mode 100644 index 0000000000..e5602397ba --- /dev/null +++ b/contrib/syntax/kate/Dockerfile.xml @@ -0,0 +1,68 @@ + + + + + + + FROM + MAINTAINER + ENV + RUN + ONBUILD + COPY + ADD + VOLUME + EXPOSE + ENTRYPOINT + CMD + WORKDIR + USER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/daemon/attach.go b/daemon/attach.go index b1b06e2765..7ccaadf442 100644 --- a/daemon/attach.go +++ b/daemon/attach.go @@ -2,14 +2,15 @@ package daemon import ( "encoding/json" - "fmt" "io" "os" "time" "github.com/docker/docker/engine" + "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/jsonlog" "github.com/docker/docker/pkg/log" + "github.com/docker/docker/pkg/promise" "github.com/docker/docker/utils" ) @@ -68,10 +69,10 @@ func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status { break } if l.Stream == "stdout" && stdout { - fmt.Fprintf(job.Stdout, "%s", l.Log) + io.WriteString(job.Stdout, l.Log) } if l.Stream == "stderr" && stderr { - fmt.Fprintf(job.Stderr, "%s", l.Log) + io.WriteString(job.Stderr, l.Log) } } } @@ -102,12 +103,11 @@ func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status { cStderr = job.Stderr } - <-daemon.Attach(container, cStdin, cStdinCloser, cStdout, cStderr) - + <-daemon.Attach(&container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, cStdin, cStdinCloser, cStdout, cStderr) // If we are in stdinonce mode, wait for the process to end // otherwise, simply return if container.Config.StdinOnce && !container.Config.Tty { - container.State.WaitStop(-1 * time.Second) + container.WaitStop(-1 * time.Second) } } return engine.StatusOK @@ -115,27 +115,29 @@ func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status { // FIXME: this should be private, and every outside subsystem // should go through the "container_attach" job. But that would require -// that job to be properly documented, as well as the relationship betweem +// that job to be properly documented, as well as the relationship between // Attach and ContainerAttach. // // This method is in use by builder/builder.go. -func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { +func (daemon *Daemon) Attach(streamConfig *StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { var ( cStdout, cStderr io.ReadCloser nJobs int errors = make(chan error, 3) ) - if stdin != nil && container.Config.OpenStdin { - nJobs += 1 - if cStdin, err := container.StdinPipe(); err != nil { + // Connect stdin of container to the http conn. + if stdin != nil && openStdin { + nJobs++ + // Get the stdin pipe. + if cStdin, err := streamConfig.StdinPipe(); err != nil { errors <- err } else { go func() { log.Debugf("attach: stdin: begin") defer log.Debugf("attach: stdin: end") // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr - if container.Config.StdinOnce && !container.Config.Tty { + if stdinOnce && !tty { defer cStdin.Close() } else { defer func() { @@ -147,10 +149,11 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo } }() } - if container.Config.Tty { + if tty { _, err = utils.CopyEscapable(cStdin, stdin) } else { _, err = io.Copy(cStdin, stdin) + } if err == io.ErrClosedPipe { err = nil @@ -163,8 +166,9 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo } } if stdout != nil { - nJobs += 1 - if p, err := container.StdoutPipe(); err != nil { + nJobs++ + // Get a reader end of a pipe that is attached as stdout to the container. + if p, err := streamConfig.StdoutPipe(); err != nil { errors <- err } else { cStdout = p @@ -172,7 +176,7 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo log.Debugf("attach: stdout: begin") defer log.Debugf("attach: stdout: end") // If we are in StdinOnce mode, then close stdin - if container.Config.StdinOnce && stdin != nil { + if stdinOnce && stdin != nil { defer stdin.Close() } if stdinCloser != nil { @@ -189,20 +193,21 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo }() } } else { + // Point stdout of container to a no-op writer. go func() { if stdinCloser != nil { defer stdinCloser.Close() } - if cStdout, err := container.StdoutPipe(); err != nil { + if cStdout, err := streamConfig.StdoutPipe(); err != nil { log.Errorf("attach: stdout pipe: %s", err) } else { - io.Copy(&utils.NopWriter{}, cStdout) + io.Copy(&ioutils.NopWriter{}, cStdout) } }() } if stderr != nil { - nJobs += 1 - if p, err := container.StderrPipe(); err != nil { + nJobs++ + if p, err := streamConfig.StderrPipe(); err != nil { errors <- err } else { cStderr = p @@ -210,7 +215,8 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo log.Debugf("attach: stderr: begin") defer log.Debugf("attach: stderr: end") // If we are in StdinOnce mode, then close stdin - if container.Config.StdinOnce && stdin != nil { + // Why are we closing stdin here and above while handling stdout? + if stdinOnce && stdin != nil { defer stdin.Close() } if stdinCloser != nil { @@ -227,20 +233,21 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo }() } } else { + // Point stderr at a no-op writer. go func() { if stdinCloser != nil { defer stdinCloser.Close() } - if cStderr, err := container.StderrPipe(); err != nil { + if cStderr, err := streamConfig.StderrPipe(); err != nil { log.Errorf("attach: stdout pipe: %s", err) } else { - io.Copy(&utils.NopWriter{}, cStderr) + io.Copy(&ioutils.NopWriter{}, cStderr) } }() } - return utils.Go(func() error { + return promise.Go(func() error { defer func() { if cStdout != nil { cStdout.Close() @@ -252,7 +259,7 @@ func (daemon *Daemon) Attach(container *Container, stdin io.ReadCloser, stdinClo // FIXME: how to clean up the stdin goroutine without the unwanted side effect // of closing the passed stdin? Add an intermediary io.Pipe? - for i := 0; i < nJobs; i += 1 { + for i := 0; i < nJobs; i++ { log.Debugf("attach: waiting for job %d/%d", i+1, nJobs) if err := <-errors; err != nil { log.Errorf("attach: job %d returned error %s, aborting all jobs", i+1, err) diff --git a/daemon/build.go b/daemon/build.go deleted file mode 100644 index a572dc2ec2..0000000000 --- a/daemon/build.go +++ /dev/null @@ -1,1008 +0,0 @@ -package daemon - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/url" - "os" - "os/exec" - "path" - "path/filepath" - "reflect" - "regexp" - "sort" - "strings" - "syscall" - "time" - - "github.com/docker/docker/archive" - "github.com/docker/docker/engine" - "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/log" - "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/symlink" - "github.com/docker/docker/pkg/system" - "github.com/docker/docker/pkg/tarsum" - "github.com/docker/docker/registry" - "github.com/docker/docker/runconfig" - "github.com/docker/docker/utils" -) - -func (daemon *Daemon) CmdBuild(job *engine.Job) engine.Status { - if len(job.Args) != 0 { - return job.Errorf("Usage: %s\n", job.Name) - } - var ( - remoteURL = job.Getenv("remote") - repoName = job.Getenv("t") - suppressOutput = job.GetenvBool("q") - noCache = job.GetenvBool("nocache") - rm = job.GetenvBool("rm") - forceRm = job.GetenvBool("forcerm") - authConfig = ®istry.AuthConfig{} - configFile = ®istry.ConfigFile{} - tag string - context io.ReadCloser - ) - job.GetenvJson("authConfig", authConfig) - job.GetenvJson("configFile", configFile) - repoName, tag = parsers.ParseRepositoryTag(repoName) - - if remoteURL == "" { - context = ioutil.NopCloser(job.Stdin) - } else if utils.IsGIT(remoteURL) { - if !strings.HasPrefix(remoteURL, "git://") { - remoteURL = "https://" + remoteURL - } - root, err := ioutil.TempDir("", "docker-build-git") - if err != nil { - return job.Error(err) - } - defer os.RemoveAll(root) - - if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil { - return job.Errorf("Error trying to use git: %s (%s)", err, output) - } - - c, err := archive.Tar(root, archive.Uncompressed) - if err != nil { - return job.Error(err) - } - context = c - } else if utils.IsURL(remoteURL) { - f, err := utils.Download(remoteURL) - if err != nil { - return job.Error(err) - } - defer f.Body.Close() - dockerFile, err := ioutil.ReadAll(f.Body) - if err != nil { - return job.Error(err) - } - c, err := archive.Generate("Dockerfile", string(dockerFile)) - if err != nil { - return job.Error(err) - } - context = c - } - defer context.Close() - - sf := utils.NewStreamFormatter(job.GetenvBool("json")) - b := NewBuildFile(daemon, daemon.eng, - &utils.StdoutFormater{ - Writer: job.Stdout, - StreamFormatter: sf, - }, - &utils.StderrFormater{ - Writer: job.Stdout, - StreamFormatter: sf, - }, - !suppressOutput, !noCache, rm, forceRm, job.Stdout, sf, authConfig, configFile) - id, err := b.Build(context) - if err != nil { - return job.Error(err) - } - if repoName != "" { - daemon.Repositories().Set(repoName, tag, id, false) - } - return engine.StatusOK -} - -var ( - ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty") -) - -type BuildFile interface { - Build(io.Reader) (string, error) - CmdFrom(string) error - CmdRun(string) error -} - -type buildFile struct { - daemon *Daemon - eng *engine.Engine - - image string - maintainer string - config *runconfig.Config - - contextPath string - context *tarsum.TarSum - - verbose bool - utilizeCache bool - rm bool - forceRm bool - - authConfig *registry.AuthConfig - configFile *registry.ConfigFile - - tmpContainers map[string]struct{} - tmpImages map[string]struct{} - - outStream io.Writer - errStream io.Writer - - // Deprecated, original writer used for ImagePull. To be removed. - outOld io.Writer - sf *utils.StreamFormatter - - // cmdSet indicates is CMD was set in current Dockerfile - cmdSet bool -} - -func (b *buildFile) clearTmp(containers map[string]struct{}) { - for c := range containers { - tmp := b.daemon.Get(c) - if err := b.daemon.Destroy(tmp); err != nil { - fmt.Fprintf(b.outStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error()) - } else { - delete(containers, c) - fmt.Fprintf(b.outStream, "Removing intermediate container %s\n", utils.TruncateID(c)) - } - } -} - -func (b *buildFile) CmdFrom(name string) error { - image, err := b.daemon.Repositories().LookupImage(name) - if err != nil { - if b.daemon.Graph().IsNotExist(err) { - remote, tag := parsers.ParseRepositoryTag(name) - pullRegistryAuth := b.authConfig - if len(b.configFile.Configs) > 0 { - // The request came with a full auth config file, we prefer to use that - endpoint, _, err := registry.ResolveRepositoryName(remote) - if err != nil { - return err - } - resolvedAuth := b.configFile.ResolveAuthConfig(endpoint) - pullRegistryAuth = &resolvedAuth - } - job := b.eng.Job("pull", remote, tag) - job.SetenvBool("json", b.sf.Json()) - job.SetenvBool("parallel", true) - job.SetenvJson("authConfig", pullRegistryAuth) - job.Stdout.Add(b.outOld) - if err := job.Run(); err != nil { - return err - } - image, err = b.daemon.Repositories().LookupImage(name) - if err != nil { - return err - } - } else { - return err - } - } - b.image = image.ID - b.config = &runconfig.Config{} - if image.Config != nil { - b.config = image.Config - } - if b.config.Env == nil || len(b.config.Env) == 0 { - b.config.Env = append(b.config.Env, "PATH="+DefaultPathEnv) - } - // Process ONBUILD triggers if they exist - if nTriggers := len(b.config.OnBuild); nTriggers != 0 { - fmt.Fprintf(b.errStream, "# Executing %d build triggers\n", nTriggers) - } - - // Copy the ONBUILD triggers, and remove them from the config, since the config will be commited. - onBuildTriggers := b.config.OnBuild - b.config.OnBuild = []string{} - - for n, step := range onBuildTriggers { - splitStep := strings.Split(step, " ") - stepInstruction := strings.ToUpper(strings.Trim(splitStep[0], " ")) - switch stepInstruction { - case "ONBUILD": - return fmt.Errorf("Source image contains forbidden chained `ONBUILD ONBUILD` trigger: %s", step) - case "MAINTAINER", "FROM": - return fmt.Errorf("Source image contains forbidden %s trigger: %s", stepInstruction, step) - } - if err := b.BuildStep(fmt.Sprintf("onbuild-%d", n), step); err != nil { - return err - } - } - return nil -} - -// The ONBUILD command declares a build instruction to be executed in any future build -// using the current image as a base. -func (b *buildFile) CmdOnbuild(trigger string) error { - splitTrigger := strings.Split(trigger, " ") - triggerInstruction := strings.ToUpper(strings.Trim(splitTrigger[0], " ")) - switch triggerInstruction { - case "ONBUILD": - return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed") - case "MAINTAINER", "FROM": - return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction) - } - b.config.OnBuild = append(b.config.OnBuild, trigger) - return b.commit("", b.config.Cmd, fmt.Sprintf("ONBUILD %s", trigger)) -} - -func (b *buildFile) CmdMaintainer(name string) error { - b.maintainer = name - return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name)) -} - -// probeCache checks to see if image-caching is enabled (`b.utilizeCache`) -// and if so attempts to look up the current `b.image` and `b.config` pair -// in the current server `b.daemon`. If an image is found, probeCache returns -// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there -// is any error, it returns `(false, err)`. -func (b *buildFile) probeCache() (bool, error) { - if b.utilizeCache { - if cache, err := b.daemon.ImageGetCached(b.image, b.config); err != nil { - return false, err - } else if cache != nil { - fmt.Fprintf(b.outStream, " ---> Using cache\n") - log.Debugf("[BUILDER] Use cached version") - b.image = cache.ID - return true, nil - } else { - log.Debugf("[BUILDER] Cache miss") - } - } - return false, nil -} - -func (b *buildFile) CmdRun(args string) error { - if b.image == "" { - return fmt.Errorf("Please provide a source image with `from` prior to run") - } - config, _, _, err := runconfig.Parse(append([]string{b.image}, b.buildCmdFromJson(args)...), nil) - if err != nil { - return err - } - - cmd := b.config.Cmd - // set Cmd manually, this is special case only for Dockerfiles - b.config.Cmd = config.Cmd - runconfig.Merge(b.config, config) - - defer func(cmd []string) { b.config.Cmd = cmd }(cmd) - - log.Debugf("Command to be executed: %v", b.config.Cmd) - - hit, err := b.probeCache() - if err != nil { - return err - } - if hit { - return nil - } - - c, err := b.create() - if err != nil { - return err - } - // Ensure that we keep the container mounted until the commit - // to avoid unmounting and then mounting directly again - c.Mount() - defer c.Unmount() - - err = b.run(c) - if err != nil { - return err - } - if err := b.commit(c.ID, cmd, "run"); err != nil { - return err - } - - return nil -} - -func (b *buildFile) FindEnvKey(key string) int { - for k, envVar := range b.config.Env { - envParts := strings.SplitN(envVar, "=", 2) - if key == envParts[0] { - return k - } - } - return -1 -} - -func (b *buildFile) ReplaceEnvMatches(value string) (string, error) { - exp, err := regexp.Compile("(\\\\\\\\+|[^\\\\]|\\b|\\A)\\$({?)([[:alnum:]_]+)(}?)") - if err != nil { - return value, err - } - matches := exp.FindAllString(value, -1) - for _, match := range matches { - match = match[strings.Index(match, "$"):] - matchKey := strings.Trim(match, "${}") - - for _, envVar := range b.config.Env { - envParts := strings.SplitN(envVar, "=", 2) - envKey := envParts[0] - envValue := envParts[1] - - if envKey == matchKey { - value = strings.Replace(value, match, envValue, -1) - break - } - } - } - return value, nil -} - -func (b *buildFile) CmdEnv(args string) error { - tmp := strings.SplitN(args, " ", 2) - if len(tmp) != 2 { - return fmt.Errorf("Invalid ENV format") - } - key := strings.Trim(tmp[0], " \t") - value := strings.Trim(tmp[1], " \t") - - envKey := b.FindEnvKey(key) - replacedValue, err := b.ReplaceEnvMatches(value) - if err != nil { - return err - } - replacedVar := fmt.Sprintf("%s=%s", key, replacedValue) - - if envKey >= 0 { - b.config.Env[envKey] = replacedVar - } else { - b.config.Env = append(b.config.Env, replacedVar) - } - return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s", replacedVar)) -} - -func (b *buildFile) buildCmdFromJson(args string) []string { - var cmd []string - if err := json.Unmarshal([]byte(args), &cmd); err != nil { - log.Debugf("Error unmarshalling: %s, setting to /bin/sh -c", err) - cmd = []string{"/bin/sh", "-c", args} - } - return cmd -} - -func (b *buildFile) CmdCmd(args string) error { - cmd := b.buildCmdFromJson(args) - b.config.Cmd = cmd - if err := b.commit("", b.config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil { - return err - } - b.cmdSet = true - return nil -} - -func (b *buildFile) CmdEntrypoint(args string) error { - entrypoint := b.buildCmdFromJson(args) - b.config.Entrypoint = entrypoint - // if there is no cmd in current Dockerfile - cleanup cmd - if !b.cmdSet { - b.config.Cmd = nil - } - if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %v", entrypoint)); err != nil { - return err - } - return nil -} - -func (b *buildFile) CmdExpose(args string) error { - portsTab := strings.Split(args, " ") - - if b.config.ExposedPorts == nil { - b.config.ExposedPorts = make(nat.PortSet) - } - ports, _, err := nat.ParsePortSpecs(append(portsTab, b.config.PortSpecs...)) - if err != nil { - return err - } - for port := range ports { - if _, exists := b.config.ExposedPorts[port]; !exists { - b.config.ExposedPorts[port] = struct{}{} - } - } - b.config.PortSpecs = nil - - return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports)) -} - -func (b *buildFile) CmdUser(args string) error { - b.config.User = args - return b.commit("", b.config.Cmd, fmt.Sprintf("USER %v", args)) -} - -func (b *buildFile) CmdInsert(args string) error { - return fmt.Errorf("INSERT has been deprecated. Please use ADD instead") -} - -func (b *buildFile) CmdCopy(args string) error { - return b.runContextCommand(args, false, false, "COPY") -} - -func (b *buildFile) CmdWorkdir(workdir string) error { - if workdir[0] == '/' { - b.config.WorkingDir = workdir - } else { - if b.config.WorkingDir == "" { - b.config.WorkingDir = "/" - } - b.config.WorkingDir = filepath.Join(b.config.WorkingDir, workdir) - } - return b.commit("", b.config.Cmd, fmt.Sprintf("WORKDIR %v", workdir)) -} - -func (b *buildFile) CmdVolume(args string) error { - if args == "" { - return fmt.Errorf("Volume cannot be empty") - } - - var volume []string - if err := json.Unmarshal([]byte(args), &volume); err != nil { - volume = []string{args} - } - if b.config.Volumes == nil { - b.config.Volumes = map[string]struct{}{} - } - for _, v := range volume { - b.config.Volumes[v] = struct{}{} - } - if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil { - return err - } - return nil -} - -func (b *buildFile) checkPathForAddition(orig string) error { - origPath := path.Join(b.contextPath, orig) - if p, err := filepath.EvalSymlinks(origPath); err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%s: no such file or directory", orig) - } - return err - } else { - origPath = p - } - if !strings.HasPrefix(origPath, b.contextPath) { - return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath) - } - _, err := os.Stat(origPath) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%s: no such file or directory", orig) - } - return err - } - return nil -} - -func (b *buildFile) addContext(container *Container, orig, dest string, decompress bool) error { - var ( - err error - destExists = true - origPath = path.Join(b.contextPath, orig) - destPath = path.Join(container.RootfsPath(), dest) - ) - - if destPath != container.RootfsPath() { - destPath, err = symlink.FollowSymlinkInScope(destPath, container.RootfsPath()) - if err != nil { - return err - } - } - - // Preserve the trailing '/' - if strings.HasSuffix(dest, "/") || dest == "." { - destPath = destPath + "/" - } - - destStat, err := os.Stat(destPath) - if err != nil { - if !os.IsNotExist(err) { - return err - } - destExists = false - } - - fi, err := os.Stat(origPath) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%s: no such file or directory", orig) - } - return err - } - - if fi.IsDir() { - return copyAsDirectory(origPath, destPath, destExists) - } - - // If we are adding a remote file (or we've been told not to decompress), do not try to untar it - if decompress { - // First try to unpack the source as an archive - // to support the untar feature we need to clean up the path a little bit - // because tar is very forgiving. First we need to strip off the archive's - // filename from the path but this is only added if it does not end in / . - tarDest := destPath - if strings.HasSuffix(tarDest, "/") { - tarDest = filepath.Dir(destPath) - } - - // try to successfully untar the orig - if err := archive.UntarPath(origPath, tarDest); err == nil { - return nil - } else if err != io.EOF { - log.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err) - } - } - - if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil { - return err - } - if err := archive.CopyWithTar(origPath, destPath); err != nil { - return err - } - - resPath := destPath - if destExists && destStat.IsDir() { - resPath = path.Join(destPath, path.Base(origPath)) - } - - return fixPermissions(resPath, 0, 0) -} - -func (b *buildFile) runContextCommand(args string, allowRemote bool, allowDecompression bool, cmdName string) error { - if b.context == nil { - return fmt.Errorf("No context given. Impossible to use %s", cmdName) - } - tmp := strings.SplitN(args, " ", 2) - if len(tmp) != 2 { - return fmt.Errorf("Invalid %s format", cmdName) - } - - orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t")) - if err != nil { - return err - } - - dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t")) - if err != nil { - return err - } - - cmd := b.config.Cmd - b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)} - defer func(cmd []string) { b.config.Cmd = cmd }(cmd) - b.config.Image = b.image - - var ( - origPath = orig - destPath = dest - remoteHash string - isRemote bool - decompress = true - ) - - isRemote = utils.IsURL(orig) - if isRemote && !allowRemote { - return fmt.Errorf("Source can't be an URL for %s", cmdName) - } else if utils.IsURL(orig) { - // Initiate the download - resp, err := utils.Download(orig) - if err != nil { - return err - } - - // Create a tmp dir - tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote") - if err != nil { - return err - } - - // Create a tmp file within our tmp dir - tmpFileName := path.Join(tmpDirName, "tmp") - tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) - if err != nil { - return err - } - defer os.RemoveAll(tmpDirName) - - // Download and dump result to tmp file - if _, err := io.Copy(tmpFile, resp.Body); err != nil { - tmpFile.Close() - return err - } - tmpFile.Close() - - // Remove the mtime of the newly created tmp file - if err := system.UtimesNano(tmpFileName, make([]syscall.Timespec, 2)); err != nil { - return err - } - - origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName)) - - // Process the checksum - r, err := archive.Tar(tmpFileName, archive.Uncompressed) - if err != nil { - return err - } - tarSum := &tarsum.TarSum{Reader: r, DisableCompression: true} - if _, err := io.Copy(ioutil.Discard, tarSum); err != nil { - return err - } - remoteHash = tarSum.Sum(nil) - r.Close() - - // If the destination is a directory, figure out the filename. - if strings.HasSuffix(dest, "/") { - u, err := url.Parse(orig) - if err != nil { - return err - } - path := u.Path - if strings.HasSuffix(path, "/") { - path = path[:len(path)-1] - } - parts := strings.Split(path, "/") - filename := parts[len(parts)-1] - if filename == "" { - return fmt.Errorf("cannot determine filename from url: %s", u) - } - destPath = dest + filename - } - } - - if err := b.checkPathForAddition(origPath); err != nil { - return err - } - - // Hash path and check the cache - if b.utilizeCache { - var ( - hash string - sums = b.context.GetSums() - ) - - if remoteHash != "" { - hash = remoteHash - } else if fi, err := os.Stat(path.Join(b.contextPath, origPath)); err != nil { - return err - } else if fi.IsDir() { - var subfiles []string - for file, sum := range sums { - absFile := path.Join(b.contextPath, file) - absOrigPath := path.Join(b.contextPath, origPath) - if strings.HasPrefix(absFile, absOrigPath) { - subfiles = append(subfiles, sum) - } - } - sort.Strings(subfiles) - hasher := sha256.New() - hasher.Write([]byte(strings.Join(subfiles, ","))) - hash = "dir:" + hex.EncodeToString(hasher.Sum(nil)) - } else { - if origPath[0] == '/' && len(origPath) > 1 { - origPath = origPath[1:] - } - origPath = strings.TrimPrefix(origPath, "./") - if h, ok := sums[origPath]; ok { - hash = "file:" + h - } - } - b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, hash, dest)} - hit, err := b.probeCache() - if err != nil { - return err - } - // If we do not have a hash, never use the cache - if hit && hash != "" { - return nil - } - } - - // Create the container - container, _, err := b.daemon.Create(b.config, "") - if err != nil { - return err - } - b.tmpContainers[container.ID] = struct{}{} - - if err := container.Mount(); err != nil { - return err - } - defer container.Unmount() - - if !allowDecompression || isRemote { - decompress = false - } - if err := b.addContext(container, origPath, destPath, decompress); err != nil { - return err - } - - if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, orig, dest)); err != nil { - return err - } - return nil -} - -func (b *buildFile) CmdAdd(args string) error { - return b.runContextCommand(args, true, true, "ADD") -} - -func (b *buildFile) create() (*Container, error) { - if b.image == "" { - return nil, fmt.Errorf("Please provide a source image with `from` prior to run") - } - b.config.Image = b.image - - // Create the container - c, _, err := b.daemon.Create(b.config, "") - if err != nil { - return nil, err - } - b.tmpContainers[c.ID] = struct{}{} - fmt.Fprintf(b.outStream, " ---> Running in %s\n", utils.TruncateID(c.ID)) - - // override the entry point that may have been picked up from the base image - c.Path = b.config.Cmd[0] - c.Args = b.config.Cmd[1:] - - return c, nil -} - -func (b *buildFile) run(c *Container) error { - var errCh chan error - if b.verbose { - errCh = utils.Go(func() error { - // FIXME: call the 'attach' job so that daemon.Attach can be made private - // - // FIXME (LK4D4): Also, maybe makes sense to call "logs" job, it is like attach - // but without hijacking for stdin. Also, with attach there can be race - // condition because of some output already was printed before it. - return <-b.daemon.Attach(c, nil, nil, b.outStream, b.errStream) - }) - } - - //start the container - if err := c.Start(); err != nil { - return err - } - - if errCh != nil { - if err := <-errCh; err != nil { - return err - } - } - - // Wait for it to finish - if ret, _ := c.State.WaitStop(-1 * time.Second); ret != 0 { - err := &utils.JSONError{ - Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.config.Cmd, ret), - Code: ret, - } - return err - } - - return nil -} - -// Commit the container with the autorun command -func (b *buildFile) commit(id string, autoCmd []string, comment string) error { - if b.image == "" { - return fmt.Errorf("Please provide a source image with `from` prior to commit") - } - b.config.Image = b.image - if id == "" { - cmd := b.config.Cmd - b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment} - defer func(cmd []string) { b.config.Cmd = cmd }(cmd) - - hit, err := b.probeCache() - if err != nil { - return err - } - if hit { - return nil - } - - container, warnings, err := b.daemon.Create(b.config, "") - if err != nil { - return err - } - for _, warning := range warnings { - fmt.Fprintf(b.outStream, " ---> [Warning] %s\n", warning) - } - b.tmpContainers[container.ID] = struct{}{} - fmt.Fprintf(b.outStream, " ---> Running in %s\n", utils.TruncateID(container.ID)) - id = container.ID - - if err := container.Mount(); err != nil { - return err - } - defer container.Unmount() - } - container := b.daemon.Get(id) - if container == nil { - return fmt.Errorf("An error occured while creating the container") - } - - // Note: Actually copy the struct - autoConfig := *b.config - autoConfig.Cmd = autoCmd - // Commit the container - image, err := b.daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig) - if err != nil { - return err - } - b.tmpImages[image.ID] = struct{}{} - b.image = image.ID - return nil -} - -// Long lines can be split with a backslash -var lineContinuation = regexp.MustCompile(`\\\s*\n`) - -func (b *buildFile) Build(context io.Reader) (string, error) { - tmpdirPath, err := ioutil.TempDir("", "docker-build") - if err != nil { - return "", err - } - - decompressedStream, err := archive.DecompressStream(context) - if err != nil { - return "", err - } - - b.context = &tarsum.TarSum{Reader: decompressedStream, DisableCompression: true} - if err := archive.Untar(b.context, tmpdirPath, nil); err != nil { - return "", err - } - defer os.RemoveAll(tmpdirPath) - - b.contextPath = tmpdirPath - filename := path.Join(tmpdirPath, "Dockerfile") - if _, err := os.Stat(filename); os.IsNotExist(err) { - return "", fmt.Errorf("Can't build a directory with no Dockerfile") - } - fileBytes, err := ioutil.ReadFile(filename) - if err != nil { - return "", err - } - if len(fileBytes) == 0 { - return "", ErrDockerfileEmpty - } - var ( - dockerfile = lineContinuation.ReplaceAllString(stripComments(fileBytes), "") - stepN = 0 - ) - for _, line := range strings.Split(dockerfile, "\n") { - line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n") - if len(line) == 0 { - continue - } - if err := b.BuildStep(fmt.Sprintf("%d", stepN), line); err != nil { - if b.forceRm { - b.clearTmp(b.tmpContainers) - } - return "", err - } else if b.rm { - b.clearTmp(b.tmpContainers) - } - stepN += 1 - } - if b.image != "" { - fmt.Fprintf(b.outStream, "Successfully built %s\n", utils.TruncateID(b.image)) - return b.image, nil - } - return "", fmt.Errorf("No image was generated. This may be because the Dockerfile does not, like, do anything.\n") -} - -// BuildStep parses a single build step from `instruction` and executes it in the current context. -func (b *buildFile) BuildStep(name, expression string) error { - fmt.Fprintf(b.outStream, "Step %s : %s\n", name, expression) - tmp := strings.SplitN(expression, " ", 2) - if len(tmp) != 2 { - return fmt.Errorf("Invalid Dockerfile format") - } - instruction := strings.ToLower(strings.Trim(tmp[0], " ")) - arguments := strings.Trim(tmp[1], " ") - - method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:])) - if !exists { - fmt.Fprintf(b.errStream, "# Skipping unknown instruction %s\n", strings.ToUpper(instruction)) - return nil - } - - ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface() - if ret != nil { - return ret.(error) - } - - fmt.Fprintf(b.outStream, " ---> %s\n", utils.TruncateID(b.image)) - return nil -} - -func stripComments(raw []byte) string { - var ( - out []string - lines = strings.Split(string(raw), "\n") - ) - for _, l := range lines { - if len(l) == 0 || l[0] == '#' { - continue - } - out = append(out, l) - } - return strings.Join(out, "\n") -} - -func copyAsDirectory(source, destination string, destinationExists bool) error { - if err := archive.CopyWithTar(source, destination); err != nil { - return err - } - - if destinationExists { - files, err := ioutil.ReadDir(source) - if err != nil { - return err - } - - for _, file := range files { - if err := fixPermissions(filepath.Join(destination, file.Name()), 0, 0); err != nil { - return err - } - } - return nil - } - - return fixPermissions(destination, 0, 0) -} - -func fixPermissions(destination string, uid, gid int) error { - return filepath.Walk(destination, func(path string, info os.FileInfo, err error) error { - if err := os.Lchown(path, uid, gid); err != nil && !os.IsNotExist(err) { - return err - } - return nil - }) -} - -func NewBuildFile(d *Daemon, eng *engine.Engine, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, forceRm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile { - return &buildFile{ - daemon: d, - eng: eng, - config: &runconfig.Config{}, - outStream: outStream, - errStream: errStream, - tmpContainers: make(map[string]struct{}), - tmpImages: make(map[string]struct{}), - verbose: verbose, - utilizeCache: utilizeCache, - rm: rm, - forceRm: forceRm, - sf: sf, - authConfig: auth, - configFile: authConfigFile, - outOld: outOld, - } -} diff --git a/daemon/config.go b/daemon/config.go index a396bd0232..8780294ce1 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -10,7 +10,7 @@ import ( const ( defaultNetworkMtu = 1500 - DisableNetworkBridge = "none" + disableNetworkBridge = "none" ) // Config define the configuration of a docker daemon @@ -23,11 +23,14 @@ type Config struct { AutoRestart bool Dns []string DnsSearch []string + Mirrors []string EnableIptables bool EnableIpForward bool + EnableIpMasq bool DefaultIp net.IP BridgeIface string BridgeIP string + FixedCIDR string InterContainerCommunication bool GraphDriver string GraphOptions []string @@ -45,11 +48,13 @@ type Config struct { func (config *Config) InstallFlags() { flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file") flag.StringVar(&config.Root, []string{"g", "-graph"}, "/var/lib/docker", "Path to use as the root of the Docker runtime") - flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated infavor of --restart policies on docker run") + flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run") flag.BoolVar(&config.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable Docker's addition of iptables rules") flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward") + flag.BoolVar(&config.EnableIpMasq, []string{"-ip-masq"}, true, "Enable IP masquerading for bridge's IP range") flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b") flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking") + flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)\nthis subnet must be nested in the bridge subnet (which is defined by -b or --bip)") flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication") flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver") flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver") @@ -60,6 +65,7 @@ func (config *Config) InstallFlags() { // FIXME: why the inconsistency between "hosts" and "sockets"? opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "Force Docker to use specific DNS servers") opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains") + opts.MirrorListVar(&config.Mirrors, []string{"-registry-mirror"}, "Specify a preferred Docker registry mirror") } func GetDefaultNetworkMtu() int { diff --git a/daemon/container.go b/daemon/container.go index df6bd66190..6fd4507972 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -1,6 +1,7 @@ package daemon import ( + "bytes" "encoding/json" "errors" "fmt" @@ -10,24 +11,24 @@ import ( "path" "path/filepath" "strings" - "sync" "syscall" "time" "github.com/docker/libcontainer/devices" "github.com/docker/libcontainer/label" - "github.com/docker/docker/archive" "github.com/docker/docker/daemon/execdriver" - "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/engine" "github.com/docker/docker/image" "github.com/docker/docker/links" "github.com/docker/docker/nat" + "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/broadcastwriter" + "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/log" "github.com/docker/docker/pkg/networkfs/etchosts" "github.com/docker/docker/pkg/networkfs/resolvconf" + "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/runconfig" "github.com/docker/docker/utils" @@ -42,10 +43,17 @@ var ( ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.") ) +type StreamConfig struct { + stdout *broadcastwriter.BroadcastWriter + stderr *broadcastwriter.BroadcastWriter + stdin io.ReadCloser + stdinPipe io.WriteCloser +} + type Container struct { - sync.Mutex - root string // Path to the "home" of the container, including metadata. - basefs string // Path to the graphdriver mountpoint + *State `json:"State"` // Needed for remote api version <= 1.11 + root string // Path to the "home" of the container, including metadata. + basefs string // Path to the graphdriver mountpoint ID string @@ -55,7 +63,6 @@ type Container struct { Args []string Config *runconfig.Config - State *State Image string NetworkSettings *NetworkSettings @@ -67,24 +74,26 @@ type Container struct { Driver string ExecDriver string - command *execdriver.Command - stdout *broadcastwriter.BroadcastWriter - stderr *broadcastwriter.BroadcastWriter - stdin io.ReadCloser - stdinPipe io.WriteCloser + command *execdriver.Command + StreamConfig daemon *Daemon MountLabel, ProcessLabel string + AppArmorProfile string RestartCount int + // Maps container paths to volume paths. The key in this is the path to which + // the volume is being mounted inside the container. Value is the path of the + // volume on disk Volumes map[string]string // Store rw/ro in a separate structure to preserve reverse-compatibility on-disk. // Easier than migrating older container configs :) VolumesRW map[string]bool hostConfig *runconfig.HostConfig - activeLinks map[string]*links.Link - monitor *containerMonitor + activeLinks map[string]*links.Link + monitor *containerMonitor + execCommands *execStore } func (container *Container) FromDisk() error { @@ -189,14 +198,7 @@ func (container *Container) getRootResourcePath(path string) (string, error) { } func populateCommand(c *Container, env []string) error { - var ( - en *execdriver.Network - context = make(map[string][]string) - ) - context["process_label"] = []string{c.GetProcessLabel()} - context["mount_label"] = []string{c.GetMountLabel()} - - en = &execdriver.Network{ + en := &execdriver.Network{ Mtu: c.daemon.config.Mtu, Interface: nil, } @@ -214,6 +216,7 @@ func populateCommand(c *Container, env []string) error { Bridge: network.Bridge, IPAddress: network.IPAddress, IPPrefixLen: network.IPPrefixLen, + MacAddress: network.MacAddress, } } case "container": @@ -230,10 +233,10 @@ func populateCommand(c *Container, env []string) error { userSpecifiedDevices := make([]*devices.Device, len(c.hostConfig.Devices)) for i, deviceMapping := range c.hostConfig.Devices { device, err := devices.GetDevice(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions) - device.Path = deviceMapping.PathInContainer if err != nil { - return fmt.Errorf("error gathering device information while adding custom device %s", err) + return fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err) } + device.Path = deviceMapping.PathInContainer userSpecifiedDevices[i] = device } allowedDevices := append(devices.DefaultAllowedDevices, userSpecifiedDevices...) @@ -241,7 +244,7 @@ func populateCommand(c *Container, env []string) error { autoCreatedDevices := append(devices.DefaultAutoCreatedDevices, userSpecifiedDevices...) // TODO: this can be removed after lxc-conf is fully deprecated - mergeLxcConfIntoOptions(c.hostConfig, context) + lxcConfig := mergeLxcConfIntoOptions(c.hostConfig) resources := &execdriver.Resources{ Memory: c.Config.Memory, @@ -249,26 +252,36 @@ func populateCommand(c *Container, env []string) error { CpuShares: c.Config.CpuShares, Cpuset: c.Config.Cpuset, } + + processConfig := execdriver.ProcessConfig{ + Privileged: c.hostConfig.Privileged, + Entrypoint: c.Path, + Arguments: c.Args, + Tty: c.Config.Tty, + User: c.Config.User, + } + + processConfig.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + processConfig.Env = env + c.command = &execdriver.Command{ ID: c.ID, - Privileged: c.hostConfig.Privileged, Rootfs: c.RootfsPath(), InitPath: "/.dockerinit", - Entrypoint: c.Path, - Arguments: c.Args, WorkingDir: c.Config.WorkingDir, Network: en, - Tty: c.Config.Tty, - User: c.Config.User, - Config: context, Resources: resources, AllowedDevices: allowedDevices, AutoCreatedDevices: autoCreatedDevices, CapAdd: c.hostConfig.CapAdd, CapDrop: c.hostConfig.CapDrop, + ProcessConfig: processConfig, + ProcessLabel: c.GetProcessLabel(), + MountLabel: c.GetMountLabel(), + LxcConfig: lxcConfig, + AppArmorProfile: c.AppArmorProfile, } - c.command.SysProcAttr = &syscall.SysProcAttr{Setsid: true} - c.command.Env = env + return nil } @@ -276,7 +289,7 @@ func (container *Container) Start() (err error) { container.Lock() defer container.Unlock() - if container.State.IsRunning() { + if container.Running { return nil } @@ -297,8 +310,11 @@ func (container *Container) Start() (err error) { if err := container.initializeNetworking(); err != nil { return err } + if err := container.updateParentsHosts(); err != nil { + return err + } container.verifyDaemonSettings() - if err := prepareVolumesForContainer(container); err != nil { + if err := container.prepareVolumes(); err != nil { return err } linkedEnv, err := container.setupLinkedContainers() @@ -312,7 +328,7 @@ func (container *Container) Start() (err error) { if err := populateCommand(container, env); err != nil { return err } - if err := setupMountsForContainer(container); err != nil { + if err := container.setupMounts(); err != nil { return err } @@ -323,7 +339,7 @@ func (container *Container) Run() error { if err := container.Start(); err != nil { return err } - container.State.WaitStop(-1 * time.Second) + container.WaitStop(-1 * time.Second) return nil } @@ -337,11 +353,11 @@ func (container *Container) Output() (output []byte, err error) { return nil, err } output, err = ioutil.ReadAll(pipe) - container.State.WaitStop(-1 * time.Second) + container.WaitStop(-1 * time.Second) return output, err } -// Container.StdinPipe returns a WriteCloser which can be used to feed data +// StreamConfig.StdinPipe returns a WriteCloser which can be used to feed data // to the standard input of the container's active process. // Container.StdoutPipe and Container.StderrPipe each return a ReadCloser // which can be used to retrieve the standard output (and error) generated @@ -349,32 +365,32 @@ func (container *Container) Output() (output []byte, err error) { // copied and delivered to all StdoutPipe and StderrPipe consumers, using // a kind of "broadcaster". -func (container *Container) StdinPipe() (io.WriteCloser, error) { - return container.stdinPipe, nil +func (streamConfig *StreamConfig) StdinPipe() (io.WriteCloser, error) { + return streamConfig.stdinPipe, nil } -func (container *Container) StdoutPipe() (io.ReadCloser, error) { +func (streamConfig *StreamConfig) StdoutPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() - container.stdout.AddWriter(writer, "") - return utils.NewBufReader(reader), nil + streamConfig.stdout.AddWriter(writer, "") + return ioutils.NewBufReader(reader), nil } -func (container *Container) StderrPipe() (io.ReadCloser, error) { +func (streamConfig *StreamConfig) StderrPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() - container.stderr.AddWriter(writer, "") - return utils.NewBufReader(reader), nil + streamConfig.stderr.AddWriter(writer, "") + return ioutils.NewBufReader(reader), nil } -func (container *Container) StdoutLogPipe() io.ReadCloser { +func (streamConfig *StreamConfig) StdoutLogPipe() io.ReadCloser { reader, writer := io.Pipe() - container.stdout.AddWriter(writer, "stdout") - return utils.NewBufReader(reader) + streamConfig.stdout.AddWriter(writer, "stdout") + return ioutils.NewBufReader(reader) } -func (container *Container) StderrLogPipe() io.ReadCloser { +func (streamConfig *StreamConfig) StderrLogPipe() io.ReadCloser { reader, writer := io.Pipe() - container.stderr.AddWriter(writer, "stderr") - return utils.NewBufReader(reader) + streamConfig.stderr.AddWriter(writer, "stderr") + return ioutils.NewBufReader(reader) } func (container *Container) buildHostnameFile() error { @@ -390,10 +406,7 @@ func (container *Container) buildHostnameFile() error { return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) } -func (container *Container) buildHostnameAndHostsFiles(IP string) error { - if err := container.buildHostnameFile(); err != nil { - return err - } +func (container *Container) buildHostsFiles(IP string) error { hostsPath, err := container.getRootResourcePath("hosts") if err != nil { @@ -413,12 +426,25 @@ func (container *Container) buildHostnameAndHostsFiles(IP string) error { extraContent[alias] = child.NetworkSettings.IPAddress } + for _, extraHost := range container.hostConfig.ExtraHosts { + parts := strings.Split(extraHost, ":") + extraContent[parts[0]] = parts[1] + } + return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, &extraContent) } -func (container *Container) allocateNetwork() error { +func (container *Container) buildHostnameAndHostsFiles(IP string) error { + if err := container.buildHostnameFile(); err != nil { + return err + } + + return container.buildHostsFiles(IP) +} + +func (container *Container) AllocateNetwork() error { mode := container.hostConfig.NetworkMode - if container.Config.NetworkDisabled || mode.IsContainer() || mode.IsHost() { + if container.Config.NetworkDisabled || !mode.IsPrivate() { return nil } @@ -432,16 +458,22 @@ func (container *Container) allocateNetwork() error { if env, err = job.Stdout.AddEnv(); err != nil { return err } - if err := job.Run(); err != nil { + if err = job.Run(); err != nil { return err } + // Error handling: At this point, the interface is allocated so we have to + // make sure that it is always released in case of error, otherwise we + // might leak resources. + if container.Config.PortSpecs != nil { - if err := migratePortMappings(container.Config, container.hostConfig); err != nil { + if err = migratePortMappings(container.Config, container.hostConfig); err != nil { + eng.Job("release_interface", container.ID).Run() return err } container.Config.PortSpecs = nil - if err := container.WriteHostConfig(); err != nil { + if err = container.WriteHostConfig(); err != nil { + eng.Job("release_interface", container.ID).Run() return err } } @@ -470,7 +502,8 @@ func (container *Container) allocateNetwork() error { container.NetworkSettings.PortMapping = nil for port := range portSpecs { - if err := container.allocatePort(eng, port, bindings); err != nil { + if err = container.allocatePort(eng, port, bindings); err != nil { + eng.Job("release_interface", container.ID).Run() return err } } @@ -480,12 +513,13 @@ func (container *Container) allocateNetwork() error { container.NetworkSettings.Bridge = env.Get("Bridge") container.NetworkSettings.IPAddress = env.Get("IP") container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen") + container.NetworkSettings.MacAddress = env.Get("MacAddress") container.NetworkSettings.Gateway = env.Get("Gateway") return nil } -func (container *Container) releaseNetwork() { +func (container *Container) ReleaseNetwork() { if container.Config.NetworkDisabled { return } @@ -495,10 +529,42 @@ func (container *Container) releaseNetwork() { container.NetworkSettings = &NetworkSettings{} } +func (container *Container) isNetworkAllocated() bool { + return container.NetworkSettings.IPAddress != "" +} + +func (container *Container) RestoreNetwork() error { + mode := container.hostConfig.NetworkMode + // Don't attempt a restore if we previously didn't allocate networking. + // This might be a legacy container with no network allocated, in which case the + // allocation will happen once and for all at start. + if !container.isNetworkAllocated() || container.Config.NetworkDisabled || !mode.IsPrivate() { + return nil + } + + eng := container.daemon.eng + + // Re-allocate the interface with the same IP and MAC address. + job := eng.Job("allocate_interface", container.ID) + job.Setenv("RequestedIP", container.NetworkSettings.IPAddress) + job.Setenv("RequestedMac", container.NetworkSettings.MacAddress) + if err := job.Run(); err != nil { + return err + } + + // Re-allocate any previously allocated ports. + for port := range container.NetworkSettings.Ports { + if err := container.allocatePort(eng, port, container.NetworkSettings.Ports); err != nil { + return err + } + } + return nil +} + // cleanup releases any network resources allocated to the container along with any rules // around how containers are linked together. It also unmounts the container's root filesystem. func (container *Container) cleanup() { - container.releaseNetwork() + container.ReleaseNetwork() // Disable all active links if container.activeLinks != nil { @@ -518,11 +584,11 @@ func (container *Container) KillSig(sig int) error { defer container.Unlock() // We could unpause the container for them rather than returning this error - if container.State.IsPaused() { + if container.Paused { return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID) } - if !container.State.IsRunning() { + if !container.Running { return nil } @@ -533,7 +599,7 @@ func (container *Container) KillSig(sig int) error { // if the container is currently restarting we do not need to send the signal // to the process. Telling the monitor that it should exit on it's next event // loop is enough - if container.State.IsRestarting() { + if container.Restarting { return nil } @@ -541,27 +607,27 @@ func (container *Container) KillSig(sig int) error { } func (container *Container) Pause() error { - if container.State.IsPaused() { + if container.IsPaused() { return fmt.Errorf("Container %s is already paused", container.ID) } - if !container.State.IsRunning() { + if !container.IsRunning() { return fmt.Errorf("Container %s is not running", container.ID) } return container.daemon.Pause(container) } func (container *Container) Unpause() error { - if !container.State.IsPaused() { + if !container.IsPaused() { return fmt.Errorf("Container %s is not paused", container.ID) } - if !container.State.IsRunning() { + if !container.IsRunning() { return fmt.Errorf("Container %s is not running", container.ID) } return container.daemon.Unpause(container) } func (container *Container) Kill() error { - if !container.State.IsRunning() { + if !container.IsRunning() { return nil } @@ -571,9 +637,9 @@ func (container *Container) Kill() error { } // 2. Wait for the process to die, in last resort, try to kill the process directly - if _, err := container.State.WaitStop(10 * time.Second); err != nil { + if _, err := container.WaitStop(10 * time.Second); err != nil { // Ensure that we don't kill ourselves - if pid := container.State.GetPid(); pid != 0 { + if pid := container.GetPid(); pid != 0 { log.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", utils.TruncateID(container.ID)) if err := syscall.Kill(pid, 9); err != nil { return err @@ -581,12 +647,12 @@ func (container *Container) Kill() error { } } - container.State.WaitStop(-1 * time.Second) + container.WaitStop(-1 * time.Second) return nil } func (container *Container) Stop(seconds int) error { - if !container.State.IsRunning() { + if !container.IsRunning() { return nil } @@ -599,11 +665,11 @@ func (container *Container) Stop(seconds int) error { } // 2. Wait for the process to exit on its own - if _, err := container.State.WaitStop(time.Duration(seconds) * time.Second); err != nil { + if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil { log.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) // 3. If it doesn't, then send SIGKILL if err := container.Kill(); err != nil { - container.State.WaitStop(-1 * time.Second) + container.WaitStop(-1 * time.Second) return err } } @@ -625,7 +691,7 @@ func (container *Container) Restart(seconds int) error { } func (container *Container) Resize(h, w int) error { - return container.command.Terminal.Resize(h, w) + return container.command.ProcessConfig.Terminal.Resize(h, w) } func (container *Container) ExportRw() (archive.Archive, error) { @@ -640,7 +706,7 @@ func (container *Container) ExportRw() (archive.Archive, error) { container.Unmount() return nil, err } - return utils.NewReadCloserWrapper(archive, func() error { + return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() container.Unmount() return err @@ -658,7 +724,7 @@ func (container *Container) Export() (archive.Archive, error) { container.Unmount() return nil, err } - return utils.NewReadCloserWrapper(archive, func() error { + return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() container.Unmount() return err @@ -670,10 +736,14 @@ func (container *Container) Mount() error { return container.daemon.Mount(container) } +func (container *Container) changes() ([]archive.Change, error) { + return container.daemon.Changes(container) +} + func (container *Container) Changes() ([]archive.Change, error) { container.Lock() defer container.Unlock() - return container.daemon.Changes(container) + return container.changes() } func (container *Container) GetImage() (*image.Image, error) { @@ -734,21 +804,13 @@ func (container *Container) GetSize() (int64, int64) { } defer container.Unmount() - if differ, ok := container.daemon.driver.(graphdriver.Differ); ok { - sizeRw, err = differ.DiffSize(container.ID) - if err != nil { - log.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err) - // FIXME: GetSize should return an error. Not changing it now in case - // there is a side-effect. - sizeRw = -1 - } - } else { - changes, _ := container.Changes() - if changes != nil { - sizeRw = archive.ChangesSize(container.basefs, changes) - } else { - sizeRw = -1 - } + initID := fmt.Sprintf("%s-init", container.ID) + sizeRw, err = driver.DiffSize(container.ID, initID) + if err != nil { + log.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err) + // FIXME: GetSize should return an error. Not changing it now in case + // there is a side-effect. + sizeRw = -1 } if _, err = os.Stat(container.basefs); err != nil { @@ -794,7 +856,7 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) { container.Unmount() return nil, err } - return utils.NewReadCloserWrapper(archive, func() error { + return ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() container.Unmount() return err @@ -809,7 +871,7 @@ func (container *Container) Exposes(p nat.Port) bool { } func (container *Container) GetPtyMaster() (*os.File, error) { - ttyConsole, ok := container.command.Terminal.(execdriver.TtyTerminal) + ttyConsole, ok := container.command.ProcessConfig.Terminal.(execdriver.TtyTerminal) if !ok { return nil, ErrNoTTY } @@ -858,26 +920,58 @@ func (container *Container) setupContainerDns() error { return err } - if config.NetworkMode != "host" && (len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0) { - var ( - dns = resolvconf.GetNameservers(resolvConf) - dnsSearch = resolvconf.GetSearchDomains(resolvConf) - ) - if len(config.Dns) > 0 { - dns = config.Dns - } else if len(daemon.config.Dns) > 0 { - dns = daemon.config.Dns + if config.NetworkMode != "host" { + // check configurations for any container/daemon dns settings + if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { + var ( + dns = resolvconf.GetNameservers(resolvConf) + dnsSearch = resolvconf.GetSearchDomains(resolvConf) + ) + if len(config.Dns) > 0 { + dns = config.Dns + } else if len(daemon.config.Dns) > 0 { + dns = daemon.config.Dns + } + if len(config.DnsSearch) > 0 { + dnsSearch = config.DnsSearch + } else if len(daemon.config.DnsSearch) > 0 { + dnsSearch = daemon.config.DnsSearch + } + return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch) } - if len(config.DnsSearch) > 0 { - dnsSearch = config.DnsSearch - } else if len(daemon.config.DnsSearch) > 0 { - dnsSearch = daemon.config.DnsSearch + + // replace any localhost/127.* nameservers + resolvConf = utils.RemoveLocalDns(resolvConf) + // if the resulting resolvConf is empty, use DefaultDns + if !bytes.Contains(resolvConf, []byte("nameserver")) { + log.Infof("No non localhost DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v", DefaultDns) + // prefix the default dns options with nameserver + resolvConf = append(resolvConf, []byte("\nnameserver "+strings.Join(DefaultDns, "\nnameserver "))...) } - return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch) } return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644) } +func (container *Container) updateParentsHosts() error { + parents, err := container.daemon.Parents(container.Name) + if err != nil { + return err + } + for _, cid := range parents { + if cid == "0" { + continue + } + + c := container.daemon.Get(cid) + if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() { + if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, container.Name[1:]); err != nil { + return fmt.Errorf("Failed to update /etc/hosts in parent container: %v", err) + } + } + } + return nil +} + func (container *Container) initializeNetworking() error { var err error if container.hostConfig.NetworkMode.IsHost() { @@ -910,7 +1004,8 @@ func (container *Container) initializeNetworking() error { container.HostsPath = hostsPath return ioutil.WriteFile(container.HostsPath, content, 0644) - } else if container.hostConfig.NetworkMode.IsContainer() { + } + if container.hostConfig.NetworkMode.IsContainer() { // we need to get the hosts files from the container to join nc, err := container.getNetworkedContainer() if err != nil { @@ -920,16 +1015,16 @@ func (container *Container) initializeNetworking() error { container.ResolvConfPath = nc.ResolvConfPath container.Config.Hostname = nc.Config.Hostname container.Config.Domainname = nc.Config.Domainname - } else if container.daemon.config.DisableNetwork { + return nil + } + if container.daemon.config.DisableNetwork { container.Config.NetworkDisabled = true return container.buildHostnameAndHostsFiles("127.0.1.1") - } else { - if err := container.allocateNetwork(); err != nil { - return err - } - return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress) } - return nil + if err := container.AllocateNetwork(); err != nil { + return err + } + return container.buildHostnameAndHostsFiles(container.NetworkSettings.IPAddress) } // Make sure the config is compatible with the current kernel @@ -970,7 +1065,7 @@ func (container *Container) setupLinkedContainers() ([]string, error) { } for linkAlias, child := range children { - if !child.State.IsRunning() { + if !child.IsRunning() { return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias) } @@ -1002,10 +1097,15 @@ func (container *Container) setupLinkedContainers() ([]string, error) { } func (container *Container) createDaemonEnvironment(linkedEnv []string) []string { + // if a domain name was specified, append it to the hostname (see #7851) + fullHostname := container.Config.Hostname + if container.Config.Domainname != "" { + fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname) + } // Setup environment env := []string{ "PATH=" + DefaultPathEnv, - "HOSTNAME=" + container.Config.Hostname, + "HOSTNAME=" + fullHostname, // Note: we don't set HOME here because it'll get autoset intelligently // based on the value of USER inside dockerinit, but only if it isn't // set already (ie, that can be overridden by setting HOME via -e or ENV @@ -1074,7 +1174,7 @@ func (container *Container) waitForStart() error { // process or until the process is running in the container select { case <-container.monitor.startSignal: - case err := <-utils.Go(container.monitor.Start): + case err := <-promise.Go(container.monitor.Start): return err } @@ -1101,7 +1201,6 @@ func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bind return err } if err := job.Run(); err != nil { - eng.Job("release_interface", container.ID).Run() return err } b.HostIp = portEnv.Get("HostIP") @@ -1137,7 +1236,7 @@ func (container *Container) getNetworkedContainer() (*Container, error) { if nc == nil { return nil, fmt.Errorf("no such container to join network: %s", parts[1]) } - if !nc.State.IsRunning() { + if !nc.IsRunning() { return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1]) } return nc, nil diff --git a/daemon/container_unit_test.go b/daemon/container_unit_test.go index 1b1b934f42..9599675f79 100644 --- a/daemon/container_unit_test.go +++ b/daemon/container_unit_test.go @@ -178,3 +178,20 @@ func TestGetFullName(t *testing.T) { t.Fatal("Error should not be nil") } } + +func TestValidContainerNames(t *testing.T) { + invalidNames := []string{"-rm", "&sdfsfd", "safd%sd"} + validNames := []string{"word-word", "word_word", "1weoid"} + + for _, name := range invalidNames { + if validContainerNamePattern.MatchString(name) { + t.Fatalf("%q is not a valid container name and was returned as valid.", name) + } + } + + for _, name := range validNames { + if !validContainerNamePattern.MatchString(name) { + t.Fatalf("%q is a valid container name and was returned as invalid.", name) + } + } +} diff --git a/daemon/create.go b/daemon/create.go index 3c6827eeec..e72b0ef206 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -15,8 +15,8 @@ func (daemon *Daemon) ContainerCreate(job *engine.Job) engine.Status { return job.Errorf("Usage: %s", job.Name) } config := runconfig.ContainerConfigFromJob(job) - if config.Memory != 0 && config.Memory < 524288 { - return job.Errorf("Minimum memory limit allowed is 512k") + if config.Memory != 0 && config.Memory < 4194304 { + return job.Errorf("Minimum memory limit allowed is 4MB") } if config.Memory > 0 && !daemon.SystemConfig().MemoryLimit { job.Errorf("Your kernel does not support memory limit capabilities. Limitation discarded.\n") @@ -26,7 +26,16 @@ func (daemon *Daemon) ContainerCreate(job *engine.Job) engine.Status { job.Errorf("Your kernel does not support swap limit capabilities. Limitation discarded.\n") config.MemorySwap = -1 } - container, buildWarnings, err := daemon.Create(config, name) + + var hostConfig *runconfig.HostConfig + if job.EnvExists("HostConfig") { + hostConfig = runconfig.ContainerHostConfigFromJob(job) + } else { + // Older versions of the API don't provide a HostConfig. + hostConfig = nil + } + + container, buildWarnings, err := daemon.Create(config, hostConfig, name) if err != nil { if daemon.Graph().IsNotExist(err) { _, tag := parsers.ParseRepositoryTag(config.Image) @@ -50,11 +59,12 @@ func (daemon *Daemon) ContainerCreate(job *engine.Job) engine.Status { for _, warning := range buildWarnings { job.Errorf("%s\n", warning) } + return engine.StatusOK } // Create creates a new container from the given configuration with a given name. -func (daemon *Daemon) Create(config *runconfig.Config, name string) (*Container, []string, error) { +func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.HostConfig, name string) (*Container, []string, error) { var ( container *Container warnings []string @@ -73,14 +83,19 @@ func (daemon *Daemon) Create(config *runconfig.Config, name string) (*Container, if container, err = daemon.newContainer(name, config, img); err != nil { return nil, nil, err } + if err := daemon.Register(container); err != nil { + return nil, nil, err + } if err := daemon.createRootfs(container, img); err != nil { return nil, nil, err } + if hostConfig != nil { + if err := daemon.setHostConfig(container, hostConfig); err != nil { + return nil, nil, err + } + } if err := container.ToDisk(); err != nil { return nil, nil, err } - if err := daemon.Register(container); err != nil { - return nil, nil, err - } return container, warnings, nil } diff --git a/daemon/daemon.go b/daemon/daemon.go index 811cb3391e..235788c684 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -14,7 +14,6 @@ import ( "github.com/docker/libcontainer/label" - "github.com/docker/docker/archive" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/execdriver/execdrivers" "github.com/docker/docker/daemon/execdriver/lxc" @@ -26,22 +25,25 @@ import ( "github.com/docker/docker/engine" "github.com/docker/docker/graph" "github.com/docker/docker/image" + "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/broadcastwriter" "github.com/docker/docker/pkg/graphdb" + "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/log" "github.com/docker/docker/pkg/namesgenerator" - "github.com/docker/docker/pkg/networkfs/resolvconf" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/runconfig" + "github.com/docker/docker/trust" "github.com/docker/docker/utils" + "github.com/docker/docker/volumes" ) var ( DefaultDns = []string{"8.8.8.8", "8.8.4.4"} - validContainerNameChars = `[a-zA-Z0-9_.-]` + validContainerNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]` validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`) ) @@ -84,33 +86,32 @@ type Daemon struct { repository string sysInitPath string containers *contStore + execCommands *execStore graph *graph.Graph repositories *graph.TagStore idIndex *truncindex.TruncIndex sysInfo *sysinfo.SysInfo - volumes *graph.Graph + volumes *volumes.Repository eng *engine.Engine config *Config containerGraph *graphdb.Database driver graphdriver.Driver execDriver execdriver.Driver + trustStore *trust.TrustStore } // Install installs daemon capabilities to eng. func (daemon *Daemon) Install(eng *engine.Engine) error { - // FIXME: rename "delete" to "rm" for consistency with the CLI command - // FIXME: rename ContainerDestroy to ContainerRm for consistency with the CLI command // FIXME: remove ImageDelete's dependency on Daemon, then move to graph/ for name, method := range map[string]engine.Handler{ "attach": daemon.ContainerAttach, - "build": daemon.CmdBuild, "commit": daemon.ContainerCommit, "container_changes": daemon.ContainerChanges, "container_copy": daemon.ContainerCopy, "container_inspect": daemon.ContainerInspect, "containers": daemon.Containers, "create": daemon.ContainerCreate, - "delete": daemon.ContainerDestroy, + "rm": daemon.ContainerRm, "export": daemon.ContainerExport, "info": daemon.CmdInfo, "kill": daemon.ContainerKill, @@ -124,6 +125,9 @@ func (daemon *Daemon) Install(eng *engine.Engine) error { "unpause": daemon.ContainerUnpause, "wait": daemon.ContainerWait, "image_delete": daemon.ImageDelete, // FIXME: see above + "execCreate": daemon.ContainerExecCreate, + "execStart": daemon.ContainerExecStart, + "execResize": daemon.ContainerExecResize, } { if err := eng.Register(name, method); err != nil { return err @@ -132,6 +136,9 @@ func (daemon *Daemon) Install(eng *engine.Engine) error { if err := daemon.Repositories().Install(eng); err != nil { return err } + if err := daemon.trustStore.Install(eng); err != nil { + return err + } // FIXME: this hack is necessary for legacy integration tests to access // the daemon object. eng.Hack_SetGlobalVar("httpapi.daemon", daemon) @@ -163,7 +170,11 @@ func (daemon *Daemon) containerRoot(id string) string { // Load reads the contents of a container from disk // This is typically done at startup. func (daemon *Daemon) load(id string) (*Container, error) { - container := &Container{root: daemon.containerRoot(id), State: NewState()} + container := &Container{ + root: daemon.containerRoot(id), + State: NewState(), + execCommands: newExecStore(), + } if err := container.FromDisk(); err != nil { return nil, err } @@ -204,7 +215,7 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err if container.Config.OpenStdin { container.stdin, container.stdinPipe = io.Pipe() } else { - container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin + container.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin } // done daemon.containers.Add(container.ID, container) @@ -216,11 +227,11 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err // FIXME: if the container is supposed to be running but is not, auto restart it? // if so, then we need to restart monitor and init a new lock // If the container is supposed to be running, make sure of it - if container.State.IsRunning() { + if container.IsRunning() { log.Debugf("killing old running container %s", container.ID) - existingPid := container.State.Pid - container.State.SetStopped(0) + existingPid := container.Pid + container.SetStopped(0) // We only have to handle this for lxc because the other drivers will ensure that // no processes are left when docker dies @@ -232,7 +243,7 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err ID: container.ID, } var err error - cmd.Process, err = os.FindProcess(existingPid) + cmd.ProcessConfig.Process, err = os.FindProcess(existingPid) if err != nil { log.Debugf("cannot find existing process for %d", existingPid) } @@ -252,7 +263,7 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err log.Debugf("Marking as stopped") - container.State.SetStopped(-127) + container.SetStopped(-127) if err := container.ToDisk(); err != nil { return err } @@ -366,7 +377,7 @@ func (daemon *Daemon) restore() error { for _, container := range registeredContainers { if container.hostConfig.RestartPolicy.Name == "always" || - (container.hostConfig.RestartPolicy.Name == "on-failure" && container.State.ExitCode != 0) { + (container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0) { log.Debugf("Starting container %s", container.ID) if err := container.Start(); err != nil { @@ -376,6 +387,10 @@ func (daemon *Daemon) restore() error { } } + for _, c := range registeredContainers { + c.registerVolumes() + } + if !debug { log.Infof(": done.") } @@ -498,21 +513,46 @@ func (daemon *Daemon) generateHostname(id string, config *runconfig.Config) { } } -func (daemon *Daemon) getEntrypointAndArgs(config *runconfig.Config) (string, []string) { +func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint, configCmd []string) (string, []string) { var ( entrypoint string args []string ) - if len(config.Entrypoint) != 0 { - entrypoint = config.Entrypoint[0] - args = append(config.Entrypoint[1:], config.Cmd...) + if len(configEntrypoint) != 0 { + entrypoint = configEntrypoint[0] + args = append(configEntrypoint[1:], configCmd...) } else { - entrypoint = config.Cmd[0] - args = config.Cmd[1:] + entrypoint = configCmd[0] + args = configCmd[1:] } return entrypoint, args } +func parseSecurityOpt(container *Container, config *runconfig.Config) error { + var ( + label_opts []string + err error + ) + + for _, opt := range config.SecurityOpt { + con := strings.SplitN(opt, ":", 2) + if len(con) == 1 { + return fmt.Errorf("Invalid --security-opt: %q", opt) + } + switch con[0] { + case "label": + label_opts = append(label_opts, con[1]) + case "apparmor": + container.AppArmorProfile = con[1] + default: + return fmt.Errorf("Invalid --security-opt: %q", opt) + } + } + + container.ProcessLabel, container.MountLabel, err = label.InitLabels(label_opts) + return err +} + func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) { var ( id string @@ -524,7 +564,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i } daemon.generateHostname(id, config) - entrypoint, args := daemon.getEntrypointAndArgs(config) + entrypoint, args := daemon.getEntrypointAndArgs(config.Entrypoint, config.Cmd) container := &Container{ // FIXME: we should generate the ID here instead of receiving it as an argument @@ -540,13 +580,11 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i Driver: daemon.driver.String(), ExecDriver: daemon.execDriver.Name(), State: NewState(), + execCommands: newExecStore(), } container.root = daemon.containerRoot(container.ID) - - if container.ProcessLabel, container.MountLabel, err = label.GenLabels(""); err != nil { - return nil, err - } - return container, nil + err = parseSecurityOpt(container, config) + return container, err } func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error { @@ -623,6 +661,15 @@ func (daemon *Daemon) Children(name string) (map[string]*Container, error) { return children, nil } +func (daemon *Daemon) Parents(name string) ([]string, error) { + name, err := GetFullContainerName(name) + if err != nil { + return nil, err + } + + return daemon.containerGraph.Parents(name) +} + func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error { fullName := path.Join(parent.Name, alias) if !daemon.containerGraph.Exists(fullName) { @@ -683,8 +730,10 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) if !config.EnableIptables && !config.InterContainerCommunication { return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.") } - // FIXME: DisableNetworkBidge doesn't need to be public anymore - config.DisableNetwork = config.BridgeIface == DisableNetworkBridge + if !config.EnableIptables && config.EnableIpMasq { + return nil, fmt.Errorf("You specified --iptables=false with --ipmasq=true. IP masquerading uses iptables to function. Please set --ipmasq to false or --iptables to true.") + } + config.DisableNetwork = config.BridgeIface == disableNetworkBridge // Claim the pidfile first, to avoid any and all unexpected race conditions. // Some of the init doesn't need a pidfile lock - but let's not try to be smart. @@ -699,25 +748,24 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) } // Check that the system is supported and we have sufficient privileges - // FIXME: return errors instead of calling Fatal if runtime.GOOS != "linux" { - log.Fatalf("The Docker daemon is only supported on linux") + return nil, fmt.Errorf("The Docker daemon is only supported on linux") } if os.Geteuid() != 0 { - log.Fatalf("The Docker daemon needs to be run as root") + return nil, fmt.Errorf("The Docker daemon needs to be run as root") } if err := checkKernelAndArch(); err != nil { - log.Fatalf(err.Error()) + return nil, err } // set up the TempDir to use a canonical path tmp, err := utils.TempDir(config.Root) if err != nil { - log.Fatalf("Unable to get the TempDir under %s: %s", config.Root, err) + return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err) } realTmp, err := utils.ReadSymlinkedDirectory(tmp) if err != nil { - log.Fatalf("Unable to get the full path to the TempDir (%s): %s", tmp, err) + return nil, fmt.Errorf("Unable to get the full path to the TempDir (%s): %s", tmp, err) } os.Setenv("TMPDIR", realTmp) if !config.EnableSelinuxSupport { @@ -731,7 +779,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) } else { realRoot, err = utils.ReadSymlinkedDirectory(config.Root) if err != nil { - log.Fatalf("Unable to get the full path to root (%s): %s", config.Root, err) + return nil, fmt.Errorf("Unable to get the full path to root (%s): %s", config.Root, err) } } config.Root = realRoot @@ -751,7 +799,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) log.Debugf("Using graph driver %s", driver) // As Docker on btrfs and SELinux are incompatible at present, error on both being enabled - if config.EnableSelinuxSupport && driver.String() == "btrfs" { + if selinuxEnabled() && config.EnableSelinuxSupport && driver.String() == "btrfs" { return nil, fmt.Errorf("SELinux is not supported with the BTRFS graph driver!") } @@ -772,31 +820,41 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) return nil, err } - // We don't want to use a complex driver like aufs or devmapper - // for volumes, just a plain filesystem volumesDriver, err := graphdriver.GetDriver("vfs", config.Root, config.GraphOptions) if err != nil { return nil, err } - log.Debugf("Creating volumes graph") - volumes, err := graph.NewGraph(path.Join(config.Root, "volumes"), volumesDriver) + + volumes, err := volumes.NewRepository(path.Join(config.Root, "volumes"), volumesDriver) if err != nil { return nil, err } + log.Debugf("Creating repository list") - repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g) + repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g, config.Mirrors) if err != nil { return nil, fmt.Errorf("Couldn't create Tag store: %s", err) } + trustDir := path.Join(config.Root, "trust") + if err := os.MkdirAll(trustDir, 0700); err != nil && !os.IsExist(err) { + return nil, err + } + t, err := trust.NewTrustStore(trustDir) + if err != nil { + return nil, fmt.Errorf("could not create trust store: %s", err) + } + if !config.DisableNetwork { job := eng.Job("init_networkdriver") job.SetenvBool("EnableIptables", config.EnableIptables) job.SetenvBool("InterContainerCommunication", config.InterContainerCommunication) job.SetenvBool("EnableIpForward", config.EnableIpForward) + job.SetenvBool("EnableIpMasq", config.EnableIpMasq) job.Setenv("BridgeIface", config.BridgeIface) job.Setenv("BridgeIP", config.BridgeIP) + job.Setenv("FixedCIDR", config.FixedCIDR) job.Setenv("DefaultBindingIP", config.DefaultIp.String()) if err := job.Run(); err != nil { @@ -839,6 +897,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) daemon := &Daemon{ repository: daemonRepo, containers: &contStore{s: make(map[string]*Container)}, + execCommands: newExecStore(), graph: g, repositories: repositories, idIndex: truncindex.NewTruncIndex([]string{}), @@ -850,9 +909,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine) (*Daemon, error) sysInitPath: sysInitPath, execDriver: ed, eng: eng, - } - if err := daemon.checkLocaldns(); err != nil { - return nil, err + trustStore: t, } if err := daemon.restore(); err != nil { return nil, err @@ -885,7 +942,7 @@ func (daemon *Daemon) shutdown() error { log.Debugf("starting clean shutdown of all containers...") for _, container := range daemon.List() { c := container - if c.State.IsRunning() { + if c.IsRunning() { log.Debugf("stopping %s", c.ID) group.Add(1) @@ -894,7 +951,7 @@ func (daemon *Daemon) shutdown() error { if err := c.KillSig(15); err != nil { log.Debugf("kill 15 error for %s - %s", c.ID, err) } - c.State.WaitStop(-1 * time.Second) + c.WaitStop(-1 * time.Second) log.Debugf("container stopped %s", c.ID) }() } @@ -924,46 +981,13 @@ func (daemon *Daemon) Unmount(container *Container) error { } func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) { - if differ, ok := daemon.driver.(graphdriver.Differ); ok { - return differ.Changes(container.ID) - } - cDir, err := daemon.driver.Get(container.ID, "") - if err != nil { - return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err) - } - defer daemon.driver.Put(container.ID) - initDir, err := daemon.driver.Get(container.ID+"-init", "") - if err != nil { - return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err) - } - defer daemon.driver.Put(container.ID + "-init") - return archive.ChangesDirs(cDir, initDir) + initID := fmt.Sprintf("%s-init", container.ID) + return daemon.driver.Changes(container.ID, initID) } func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) { - if differ, ok := daemon.driver.(graphdriver.Differ); ok { - return differ.Diff(container.ID) - } - - changes, err := daemon.Changes(container) - if err != nil { - return nil, err - } - - cDir, err := daemon.driver.Get(container.ID, "") - if err != nil { - return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.daemon.driver, err) - } - - archive, err := archive.ExportChanges(cDir, changes) - if err != nil { - return nil, err - } - return utils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() - daemon.driver.Put(container.ID) - return err - }), nil + initID := fmt.Sprintf("%s-init", container.ID) + return daemon.driver.Diff(container.ID, initID) } func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { @@ -974,7 +998,7 @@ func (daemon *Daemon) Pause(c *Container) error { if err := daemon.execDriver.Pause(c.command); err != nil { return err } - c.State.SetPaused() + c.SetPaused() return nil } @@ -982,7 +1006,7 @@ func (daemon *Daemon) Unpause(c *Container) error { if err := daemon.execDriver.Unpause(c.command); err != nil { return err } - c.State.SetUnpaused() + c.SetUnpaused() return nil } @@ -1042,26 +1066,10 @@ func (daemon *Daemon) ExecutionDriver() execdriver.Driver { return daemon.execDriver } -func (daemon *Daemon) Volumes() *graph.Graph { - return daemon.volumes -} - func (daemon *Daemon) ContainerGraph() *graphdb.Database { return daemon.containerGraph } -func (daemon *Daemon) checkLocaldns() error { - resolvConf, err := resolvconf.Get() - if err != nil { - return err - } - if len(daemon.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) { - log.Infof("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v", DefaultDns) - daemon.config.Dns = DefaultDns - } - return nil -} - func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) { // Retrieve all images images, err := daemon.Graph().Map() diff --git a/daemon/daemon_unit_test.go b/daemon/daemon_unit_test.go new file mode 100644 index 0000000000..f3b899ec8d --- /dev/null +++ b/daemon/daemon_unit_test.go @@ -0,0 +1,39 @@ +package daemon + +import ( + "testing" + + "github.com/docker/docker/runconfig" +) + +func TestParseSecurityOpt(t *testing.T) { + container := &Container{} + config := &runconfig.Config{} + + // test apparmor + config.SecurityOpt = []string{"apparmor:test_profile"} + if err := parseSecurityOpt(container, config); err != nil { + t.Fatalf("Unexpected parseSecurityOpt error: %v", err) + } + if container.AppArmorProfile != "test_profile" { + t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile) + } + + // test valid label + config.SecurityOpt = []string{"label:user:USER"} + if err := parseSecurityOpt(container, config); err != nil { + t.Fatalf("Unexpected parseSecurityOpt error: %v", err) + } + + // test invalid label + config.SecurityOpt = []string{"label"} + if err := parseSecurityOpt(container, config); err == nil { + t.Fatal("Expected parseSecurityOpt error, got nil") + } + + // test invalid opt + config.SecurityOpt = []string{"test"} + if err := parseSecurityOpt(container, config); err == nil { + t.Fatal("Expected parseSecurityOpt error, got nil") + } +} diff --git a/daemon/delete.go b/daemon/delete.go index 501aed3e38..77be926c1c 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -4,15 +4,12 @@ import ( "fmt" "os" "path" - "path/filepath" - "strings" "github.com/docker/docker/engine" "github.com/docker/docker/pkg/log" ) -// FIXME: rename to ContainerRemove for consistency with the CLI command. -func (daemon *Daemon) ContainerDestroy(job *engine.Job) engine.Status { +func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status { if len(job.Args) != 1 { return job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name) } @@ -22,10 +19,11 @@ func (daemon *Daemon) ContainerDestroy(job *engine.Job) engine.Status { forceRemove := job.GetenvBool("forceRemove") container := daemon.Get(name) + if container == nil { + return job.Errorf("No such container: %s", name) + } + if removeLink { - if container == nil { - return job.Errorf("No such link: %s", name) - } name, err := GetFullContainerName(name) if err != nil { job.Error(err) @@ -51,7 +49,7 @@ func (daemon *Daemon) ContainerDestroy(job *engine.Job) engine.Status { } if container != nil { - if container.State.IsRunning() { + if container.IsRunning() { if forceRemove { if err := container.Kill(); err != nil { return job.Errorf("Could not kill running container, cannot remove - %v", err) @@ -64,73 +62,22 @@ func (daemon *Daemon) ContainerDestroy(job *engine.Job) engine.Status { return job.Errorf("Cannot destroy container %s: %s", name, err) } container.LogEvent("destroy") - if removeVolume { - var ( - volumes = make(map[string]struct{}) - binds = make(map[string]struct{}) - usedVolumes = make(map[string]*Container) - ) - - // the volume id is always the base of the path - getVolumeId := func(p string) string { - return filepath.Base(strings.TrimSuffix(p, "/layer")) - } - - // populate bind map so that they can be skipped and not removed - for _, bind := range container.HostConfig().Binds { - source := strings.Split(bind, ":")[0] - // TODO: refactor all volume stuff, all of it - // it is very important that we eval the link or comparing the keys to container.Volumes will not work - // - // eval symlink can fail, ref #5244 if we receive an is not exist error we can ignore it - p, err := filepath.EvalSymlinks(source) - if err != nil && !os.IsNotExist(err) { - return job.Error(err) - } - if p != "" { - source = p - } - binds[source] = struct{}{} - } - - // Store all the deleted containers volumes - for _, volumeId := range container.Volumes { - // Skip the volumes mounted from external - // bind mounts here will will be evaluated for a symlink - if _, exists := binds[volumeId]; exists { - continue - } - - volumeId = getVolumeId(volumeId) - volumes[volumeId] = struct{}{} - } - - // Retrieve all volumes from all remaining containers - for _, container := range daemon.List() { - for _, containerVolumeId := range container.Volumes { - containerVolumeId = getVolumeId(containerVolumeId) - usedVolumes[containerVolumeId] = container - } - } - - for volumeId := range volumes { - // If the requested volu - if c, exists := usedVolumes[volumeId]; exists { - log.Infof("The volume %s is used by the container %s. Impossible to remove it. Skipping.", volumeId, c.ID) - continue - } - if err := daemon.Volumes().Delete(volumeId); err != nil { - return job.Errorf("Error calling volumes.Delete(%q): %v", volumeId, err) - } - } + daemon.DeleteVolumes(container.VolumePaths()) } - } else { - return job.Errorf("No such container: %s", name) } return engine.StatusOK } +func (daemon *Daemon) DeleteVolumes(volumeIDs map[string]struct{}) { + for id := range volumeIDs { + if err := daemon.volumes.Delete(id); err != nil { + log.Infof("%s", err) + continue + } + } +} + // Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem. // FIXME: rename to Rm for consistency with the CLI command func (daemon *Daemon) Destroy(container *Container) error { @@ -150,7 +97,7 @@ func (daemon *Daemon) Destroy(container *Container) error { // Deregister the container before removing its directory, to avoid race conditions daemon.idIndex.Delete(container.ID) daemon.containers.Delete(container.ID) - + container.derefVolumes() if _, err := daemon.containerGraph.Purge(container.ID); err != nil { log.Debugf("Unable to remove container from link graph: %s", err) } @@ -168,6 +115,10 @@ func (daemon *Daemon) Destroy(container *Container) error { return fmt.Errorf("Unable to remove filesystem for %v: %v", container.ID, err) } + if err := daemon.execDriver.Clean(container.ID); err != nil { + return fmt.Errorf("Unable to remove execdriver data for %s: %s", container.ID, err) + } + selinuxFreeLxcContexts(container.ProcessLabel) return nil diff --git a/daemon/exec.go b/daemon/exec.go new file mode 100644 index 0000000000..0ab1c0bf5f --- /dev/null +++ b/daemon/exec.go @@ -0,0 +1,301 @@ +// build linux + +package daemon + +import ( + "fmt" + "io" + "io/ioutil" + "strings" + "sync" + + "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/daemon/execdriver/lxc" + "github.com/docker/docker/engine" + "github.com/docker/docker/pkg/broadcastwriter" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/log" + "github.com/docker/docker/pkg/promise" + "github.com/docker/docker/runconfig" + "github.com/docker/docker/utils" +) + +type execConfig struct { + sync.Mutex + ID string + Running bool + ProcessConfig execdriver.ProcessConfig + StreamConfig + OpenStdin bool + OpenStderr bool + OpenStdout bool + Container *Container +} + +type execStore struct { + s map[string]*execConfig + sync.Mutex +} + +func newExecStore() *execStore { + return &execStore{s: make(map[string]*execConfig, 0)} +} + +func (e *execStore) Add(id string, execConfig *execConfig) { + e.Lock() + e.s[id] = execConfig + e.Unlock() +} + +func (e *execStore) Get(id string) *execConfig { + e.Lock() + res := e.s[id] + e.Unlock() + return res +} + +func (e *execStore) Delete(id string) { + e.Lock() + delete(e.s, id) + e.Unlock() +} + +func (execConfig *execConfig) Resize(h, w int) error { + return execConfig.ProcessConfig.Terminal.Resize(h, w) +} + +func (d *Daemon) registerExecCommand(execConfig *execConfig) { + // Storing execs in container inorder to kill them gracefully whenever the container is stopped or removed. + execConfig.Container.execCommands.Add(execConfig.ID, execConfig) + // Storing execs in daemon for easy access via remote API. + d.execCommands.Add(execConfig.ID, execConfig) +} + +func (d *Daemon) getExecConfig(name string) (*execConfig, error) { + if execConfig := d.execCommands.Get(name); execConfig != nil { + if !execConfig.Container.IsRunning() { + return nil, fmt.Errorf("Container %s is not running", execConfig.Container.ID) + } + return execConfig, nil + } + + return nil, fmt.Errorf("No such exec instance '%s' found in daemon", name) +} + +func (d *Daemon) unregisterExecCommand(execConfig *execConfig) { + execConfig.Container.execCommands.Delete(execConfig.ID) + d.execCommands.Delete(execConfig.ID) +} + +func (d *Daemon) getActiveContainer(name string) (*Container, error) { + container := d.Get(name) + + if container == nil { + return nil, fmt.Errorf("No such container: %s", name) + } + + if !container.IsRunning() { + return nil, fmt.Errorf("Container %s is not running", name) + } + + return container, nil +} + +func (d *Daemon) ContainerExecCreate(job *engine.Job) engine.Status { + if len(job.Args) != 1 { + return job.Errorf("Usage: %s [options] container command [args]", job.Name) + } + + if strings.HasPrefix(d.execDriver.Name(), lxc.DriverName) { + return job.Error(lxc.ErrExec) + } + + var name = job.Args[0] + + container, err := d.getActiveContainer(name) + if err != nil { + return job.Error(err) + } + + config := runconfig.ExecConfigFromJob(job) + + entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd) + + processConfig := execdriver.ProcessConfig{ + Privileged: config.Privileged, + User: config.User, + Tty: config.Tty, + Entrypoint: entrypoint, + Arguments: args, + } + + execConfig := &execConfig{ + ID: utils.GenerateRandomID(), + OpenStdin: config.AttachStdin, + OpenStdout: config.AttachStdout, + OpenStderr: config.AttachStderr, + StreamConfig: StreamConfig{}, + ProcessConfig: processConfig, + Container: container, + Running: false, + } + + d.registerExecCommand(execConfig) + + job.Printf("%s\n", execConfig.ID) + + return engine.StatusOK +} + +func (d *Daemon) ContainerExecStart(job *engine.Job) engine.Status { + if len(job.Args) != 1 { + return job.Errorf("Usage: %s [options] exec", job.Name) + } + + var ( + cStdin io.ReadCloser + cStdout, cStderr io.Writer + cStdinCloser io.Closer + execName = job.Args[0] + ) + + execConfig, err := d.getExecConfig(execName) + if err != nil { + return job.Error(err) + } + + func() { + execConfig.Lock() + defer execConfig.Unlock() + if execConfig.Running { + err = fmt.Errorf("Error: Exec command %s is already running", execName) + } + execConfig.Running = true + }() + if err != nil { + return job.Error(err) + } + + log.Debugf("starting exec command %s in container %s", execConfig.ID, execConfig.Container.ID) + container := execConfig.Container + + if execConfig.OpenStdin { + r, w := io.Pipe() + go func() { + defer w.Close() + io.Copy(w, job.Stdin) + }() + cStdin = r + cStdinCloser = job.Stdin + } + if execConfig.OpenStdout { + cStdout = job.Stdout + } + if execConfig.OpenStderr { + cStderr = job.Stderr + } + + execConfig.StreamConfig.stderr = broadcastwriter.New() + execConfig.StreamConfig.stdout = broadcastwriter.New() + // Attach to stdin + if execConfig.OpenStdin { + execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdinPipe = io.Pipe() + } else { + execConfig.StreamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) // Silently drop stdin + } + + attachErr := d.Attach(&execConfig.StreamConfig, execConfig.OpenStdin, false, execConfig.ProcessConfig.Tty, cStdin, cStdinCloser, cStdout, cStderr) + + execErr := make(chan error) + + // Remove exec from daemon and container. + defer d.unregisterExecCommand(execConfig) + + go func() { + err := container.Exec(execConfig) + if err != nil { + execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err) + } + }() + + select { + case err := <-attachErr: + if err != nil { + return job.Errorf("attach failed with error: %s", err) + } + break + case err := <-execErr: + return job.Error(err) + } + + return engine.StatusOK +} + +func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { + return d.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback) +} + +func (container *Container) Exec(execConfig *execConfig) error { + container.Lock() + defer container.Unlock() + + waitStart := make(chan struct{}) + + callback := func(processConfig *execdriver.ProcessConfig, pid int) { + if processConfig.Tty { + // The callback is called after the process Start() + // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave + // which we close here. + if c, ok := processConfig.Stdout.(io.Closer); ok { + c.Close() + } + } + close(waitStart) + } + + // We use a callback here instead of a goroutine and an chan for + // syncronization purposes + cErr := promise.Go(func() error { return container.monitorExec(execConfig, callback) }) + + // Exec should not return until the process is actually running + select { + case <-waitStart: + case err := <-cErr: + return err + } + + return nil +} + +func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) error { + var ( + err error + exitCode int + ) + + pipes := execdriver.NewPipes(execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdout, execConfig.StreamConfig.stderr, execConfig.OpenStdin) + exitCode, err = container.daemon.Exec(container, execConfig, pipes, callback) + if err != nil { + log.Errorf("Error running command in existing container %s: %s", container.ID, err) + } + + log.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) + if execConfig.OpenStdin { + if err := execConfig.StreamConfig.stdin.Close(); err != nil { + log.Errorf("Error closing stdin while running in %s: %s", container.ID, err) + } + } + if err := execConfig.StreamConfig.stdout.Clean(); err != nil { + log.Errorf("Error closing stdout while running in %s: %s", container.ID, err) + } + if err := execConfig.StreamConfig.stderr.Clean(); err != nil { + log.Errorf("Error closing stderr while running in %s: %s", container.ID, err) + } + if execConfig.ProcessConfig.Terminal != nil { + if err := execConfig.ProcessConfig.Terminal.Close(); err != nil { + log.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) + } + } + + return err +} diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 121c6a5a03..22e4c4647c 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -20,7 +20,7 @@ var ( ErrDriverNotFound = errors.New("The requested docker init has not been found") ) -type StartCallback func(*Command) +type StartCallback func(*ProcessConfig, int) // Driver specific information based on // processes registered with the driver @@ -42,6 +42,8 @@ type TtyTerminal interface { type Driver interface { Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code + // Exec executes the process in an existing container, blocks until the process exits and returns the exit code + Exec(c *Command, processConfig *ProcessConfig, pipes *Pipes, startCallback StartCallback) (int, error) Kill(c *Command, sig int) error Pause(c *Command) error Unpause(c *Command) error @@ -49,6 +51,7 @@ type Driver interface { Info(id string) Info // "temporary" hack (until we move state from core to plugins) GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container. Terminate(c *Command) error // kill it with fire + Clean(id string) error // clean all traces of container exec } // Network settings of the container @@ -62,8 +65,9 @@ type Network struct { type NetworkInterface struct { Gateway string `json:"gateway"` IPAddress string `json:"ip"` - Bridge string `json:"bridge"` IPPrefixLen int `json:"ip_prefix_len"` + MacAddress string `json:"mac_address"` + Bridge string `json:"bridge"` } type Resources struct { @@ -78,38 +82,40 @@ type Mount struct { Destination string `json:"destination"` Writable bool `json:"writable"` Private bool `json:"private"` + Slave bool `json:"slave"` +} + +// Describes a process that will be run inside a container. +type ProcessConfig struct { + exec.Cmd `json:"-"` + + Privileged bool `json:"privileged"` + User string `json:"user"` + Tty bool `json:"tty"` + Entrypoint string `json:"entrypoint"` + Arguments []string `json:"arguments"` + Terminal Terminal `json:"-"` // standard or tty terminal + Console string `json:"-"` // dev/console path } // Process wrapps an os/exec.Cmd to add more metadata type Command struct { - exec.Cmd `json:"-"` - - ID string `json:"id"` - Privileged bool `json:"privileged"` - User string `json:"user"` - Rootfs string `json:"rootfs"` // root fs of the container - InitPath string `json:"initpath"` // dockerinit - Entrypoint string `json:"entrypoint"` - Arguments []string `json:"arguments"` - WorkingDir string `json:"working_dir"` - ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver - Tty bool `json:"tty"` - Network *Network `json:"network"` - Config map[string][]string `json:"config"` // generic values that specific drivers can consume - Resources *Resources `json:"resources"` - Mounts []Mount `json:"mounts"` - AllowedDevices []*devices.Device `json:"allowed_devices"` - AutoCreatedDevices []*devices.Device `json:"autocreated_devices"` - CapAdd []string `json:"cap_add"` - CapDrop []string `json:"cap_drop"` - - Terminal Terminal `json:"-"` // standard or tty terminal - Console string `json:"-"` // dev/console path - ContainerPid int `json:"container_pid"` // the pid for the process inside a container -} - -// Return the pid of the process -// If the process is nil -1 will be returned -func (c *Command) Pid() int { - return c.ContainerPid + ID string `json:"id"` + Rootfs string `json:"rootfs"` // root fs of the container + InitPath string `json:"initpath"` // dockerinit + WorkingDir string `json:"working_dir"` + ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver + Network *Network `json:"network"` + Resources *Resources `json:"resources"` + Mounts []Mount `json:"mounts"` + AllowedDevices []*devices.Device `json:"allowed_devices"` + AutoCreatedDevices []*devices.Device `json:"autocreated_devices"` + CapAdd []string `json:"cap_add"` + CapDrop []string `json:"cap_drop"` + ContainerPid int `json:"container_pid"` // the pid for the process inside a container + ProcessConfig ProcessConfig `json:"process_config"` // Describes the init process of the container. + ProcessLabel string `json:"process_label"` + MountLabel string `json:"mount_label"` + LxcConfig []string `json:"lxc_config"` + AppArmorProfile string `json:"apparmor_profile"` } diff --git a/daemon/execdriver/lxc/driver.go b/daemon/execdriver/lxc/driver.go index 3b870172bf..0809b05c1e 100644 --- a/daemon/execdriver/lxc/driver.go +++ b/daemon/execdriver/lxc/driver.go @@ -2,6 +2,7 @@ package lxc import ( "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -21,12 +22,13 @@ import ( "github.com/docker/docker/pkg/term" "github.com/docker/docker/utils" "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/mount/nodes" ) const DriverName = "lxc" +var ErrExec = errors.New("Unsupported: Exec is not supported by the lxc driver") + type driver struct { root string // root path for the driver to use initPath string @@ -59,12 +61,12 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba err error ) - if c.Tty { - term, err = NewTtyConsole(c, pipes) + if c.ProcessConfig.Tty { + term, err = NewTtyConsole(&c.ProcessConfig, pipes) } else { - term, err = execdriver.NewStdConsole(c, pipes) + term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes) } - c.Terminal = term + c.ProcessConfig.Terminal = term c.Mounts = append(c.Mounts, execdriver.Mount{ Source: d.initPath, @@ -98,11 +100,11 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba "-mtu", strconv.Itoa(c.Network.Mtu), ) - if c.User != "" { - params = append(params, "-u", c.User) + if c.ProcessConfig.User != "" { + params = append(params, "-u", c.ProcessConfig.User) } - if c.Privileged { + if c.ProcessConfig.Privileged { if d.apparmor { params[0] = path.Join(d.root, "lxc-start-unconfined") @@ -122,8 +124,8 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba params = append(params, fmt.Sprintf("-cap-drop=%s", strings.Join(c.CapDrop, ":"))) } - params = append(params, "--", c.Entrypoint) - params = append(params, c.Arguments...) + params = append(params, "--", c.ProcessConfig.Entrypoint) + params = append(params, c.ProcessConfig.Arguments...) if d.sharedRoot { // lxc-start really needs / to be non-shared, or all kinds of stuff break @@ -149,14 +151,14 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba if err != nil { aname = name } - c.Path = aname - c.Args = append([]string{name}, arg...) + c.ProcessConfig.Path = aname + c.ProcessConfig.Args = append([]string{name}, arg...) if err := nodes.CreateDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil { return -1, err } - if err := c.Start(); err != nil { + if err := c.ProcessConfig.Start(); err != nil { return -1, err } @@ -166,7 +168,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba ) go func() { - if err := c.Wait(); err != nil { + if err := c.ProcessConfig.Wait(); err != nil { if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0 waitErr = err } @@ -177,9 +179,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba // Poll lxc for RUNNING status pid, err := d.waitForStart(c, waitLock) if err != nil { - if c.Process != nil { - c.Process.Kill() - c.Wait() + if c.ProcessConfig.Process != nil { + c.ProcessConfig.Process.Kill() + c.ProcessConfig.Wait() } return -1, err } @@ -187,7 +189,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba c.ContainerPid = pid if startCallback != nil { - startCallback(c) + startCallback(&c.ProcessConfig, pid) } <-waitLock @@ -198,10 +200,10 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba /// Return the exit code of the process // if the process has not exited -1 will be returned func getExitCode(c *execdriver.Command) int { - if c.ProcessState == nil { + if c.ProcessConfig.ProcessState == nil { return -1 } - return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() + return c.ProcessConfig.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() } func (d *driver) Kill(c *execdriver.Command, sig int) error { @@ -407,42 +409,29 @@ func rootIsShared() bool { } func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) { - var ( - process, mount string - root = path.Join(d.root, "containers", c.ID, "config.lxc") - labels = c.Config["label"] - ) + root := path.Join(d.root, "containers", c.ID, "config.lxc") + fo, err := os.Create(root) if err != nil { return "", err } defer fo.Close() - if len(labels) > 0 { - process, mount, err = label.GenLabels(labels[0]) - if err != nil { - return "", err - } - } - if err := LxcTemplateCompiled.Execute(fo, struct { *execdriver.Command - AppArmor bool - ProcessLabel string - MountLabel string + AppArmor bool }{ - Command: c, - AppArmor: d.apparmor, - ProcessLabel: process, - MountLabel: mount, + Command: c, + AppArmor: d.apparmor, }); err != nil { return "", err } + return root, nil } func (d *driver) generateEnvConfig(c *execdriver.Command) error { - data, err := json.Marshal(c.Env) + data, err := json.Marshal(c.ProcessConfig.Env) if err != nil { return err } @@ -457,12 +446,17 @@ func (d *driver) generateEnvConfig(c *execdriver.Command) error { return ioutil.WriteFile(p, data, 0600) } +// Clean not implemented for lxc +func (d *driver) Clean(id string) error { + return nil +} + type TtyConsole struct { MasterPty *os.File SlavePty *os.File } -func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) { +func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) { // lxc is special in that we cannot create the master outside of the container without // opening the slave because we have nothing to provide to the cmd. We have to open both then do // the crazy setup on command right now instead of passing the console path to lxc and telling it @@ -478,12 +472,12 @@ func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyCo SlavePty: ptySlave, } - if err := tty.AttachPipes(&command.Cmd, pipes); err != nil { + if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil { tty.Close() return nil, err } - command.Console = tty.SlavePty.Name() + processConfig.Console = tty.SlavePty.Name() return tty, nil } @@ -527,3 +521,7 @@ func (t *TtyConsole) Close() error { t.SlavePty.Close() return t.MasterPty.Close() } + +func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { + return -1, ErrExec +} diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index 229b0a5144..2cd63dc72d 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -34,15 +34,11 @@ lxc.pts = 1024 # disable the main console lxc.console = none -{{if .ProcessLabel}} -lxc.se_context = {{ .ProcessLabel}} -{{end}} -{{$MOUNTLABEL := .MountLabel}} # no controlling tty at all lxc.tty = 1 -{{if .Privileged}} +{{if .ProcessConfig.Privileged}} lxc.cgroup.devices.allow = a {{else}} # no implicit access to devices @@ -66,12 +62,12 @@ lxc.pivotdir = lxc_putold lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0 lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0 -{{if .Tty}} -lxc.mount.entry = {{.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0 +{{if .ProcessConfig.Tty}} +lxc.mount.entry = {{.ProcessConfig.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0 {{end}} -lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts {{formatMountLabel "newinstance,ptmxmode=0666,nosuid,noexec" $MOUNTLABEL}} 0 0 -lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs {{formatMountLabel "size=65536k,nosuid,nodev,noexec" $MOUNTLABEL}} 0 0 +lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts {{formatMountLabel "newinstance,ptmxmode=0666,nosuid,noexec" ""}} 0 0 +lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs {{formatMountLabel "size=65536k,nosuid,nodev,noexec" ""}} 0 0 {{range $value := .Mounts}} {{if $value.Writable}} @@ -81,7 +77,7 @@ lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabS {{end}} {{end}} -{{if .Privileged}} +{{if .ProcessConfig.Privileged}} {{if .AppArmor}} lxc.aa_profile = unconfined {{else}} @@ -106,8 +102,8 @@ lxc.cgroup.cpuset.cpus = {{.Resources.Cpuset}} {{end}} {{end}} -{{if .Config.lxc}} -{{range $value := .Config.lxc}} +{{if .LxcConfig}} +{{range $value := .LxcConfig}} lxc.{{$value}} {{end}} {{end}} diff --git a/daemon/execdriver/lxc/lxc_template_unit_test.go b/daemon/execdriver/lxc/lxc_template_unit_test.go index 8acda804ee..900700b740 100644 --- a/daemon/execdriver/lxc/lxc_template_unit_test.go +++ b/daemon/execdriver/lxc/lxc_template_unit_test.go @@ -52,6 +52,7 @@ func TestLXCConfig(t *testing.T) { Interface: nil, }, AllowedDevices: make([]*devices.Device, 0), + ProcessConfig: execdriver.ProcessConfig{}, } p, err := driver.generateLXCConfig(command) if err != nil { @@ -77,19 +78,20 @@ func TestCustomLxcConfig(t *testing.T) { if err != nil { t.Fatal(err) } - command := &execdriver.Command{ - ID: "1", + processConfig := execdriver.ProcessConfig{ Privileged: false, - Config: map[string][]string{ - "lxc": { - "lxc.utsname = docker", - "lxc.cgroup.cpuset.cpus = 0,1", - }, + } + command := &execdriver.Command{ + ID: "1", + LxcConfig: []string{ + "lxc.utsname = docker", + "lxc.cgroup.cpuset.cpus = 0,1", }, Network: &execdriver.Network{ Mtu: 1500, Interface: nil, }, + ProcessConfig: processConfig, } p, err := driver.generateLXCConfig(command) diff --git a/daemon/execdriver/native/configuration/parse.go b/daemon/execdriver/native/configuration/parse.go deleted file mode 100644 index e021fa0de4..0000000000 --- a/daemon/execdriver/native/configuration/parse.go +++ /dev/null @@ -1,188 +0,0 @@ -package configuration - -import ( - "fmt" - "os/exec" - "path/filepath" - "strconv" - "strings" - - "github.com/docker/docker/pkg/units" - "github.com/docker/libcontainer" -) - -type Action func(*libcontainer.Config, interface{}, string) error - -var actions = map[string]Action{ - "cap.add": addCap, // add a cap - "cap.drop": dropCap, // drop a cap - - "ns.add": addNamespace, // add a namespace - "ns.drop": dropNamespace, // drop a namespace when cloning - - "net.join": joinNetNamespace, // join another containers net namespace - - "cgroups.cpu_shares": cpuShares, // set the cpu shares - "cgroups.memory": memory, // set the memory limit - "cgroups.memory_reservation": memoryReservation, // set the memory reservation - "cgroups.memory_swap": memorySwap, // set the memory swap limit - "cgroups.cpuset.cpus": cpusetCpus, // set the cpus used - - "systemd.slice": systemdSlice, // set parent Slice used for systemd unit - - "apparmor_profile": apparmorProfile, // set the apparmor profile to apply - - "fs.readonly": readonlyFs, // make the rootfs of the container read only -} - -func cpusetCpus(container *libcontainer.Config, context interface{}, value string) error { - if container.Cgroups == nil { - return fmt.Errorf("cannot set cgroups when they are disabled") - } - container.Cgroups.CpusetCpus = value - - return nil -} - -func systemdSlice(container *libcontainer.Config, context interface{}, value string) error { - if container.Cgroups == nil { - return fmt.Errorf("cannot set slice when cgroups are disabled") - } - container.Cgroups.Slice = value - - return nil -} - -func apparmorProfile(container *libcontainer.Config, context interface{}, value string) error { - container.AppArmorProfile = value - return nil -} - -func cpuShares(container *libcontainer.Config, context interface{}, value string) error { - if container.Cgroups == nil { - return fmt.Errorf("cannot set cgroups when they are disabled") - } - v, err := strconv.ParseInt(value, 10, 0) - if err != nil { - return err - } - container.Cgroups.CpuShares = v - return nil -} - -func memory(container *libcontainer.Config, context interface{}, value string) error { - if container.Cgroups == nil { - return fmt.Errorf("cannot set cgroups when they are disabled") - } - - v, err := units.RAMInBytes(value) - if err != nil { - return err - } - container.Cgroups.Memory = v - return nil -} - -func memoryReservation(container *libcontainer.Config, context interface{}, value string) error { - if container.Cgroups == nil { - return fmt.Errorf("cannot set cgroups when they are disabled") - } - - v, err := units.RAMInBytes(value) - if err != nil { - return err - } - container.Cgroups.MemoryReservation = v - return nil -} - -func memorySwap(container *libcontainer.Config, context interface{}, value string) error { - if container.Cgroups == nil { - return fmt.Errorf("cannot set cgroups when they are disabled") - } - v, err := strconv.ParseInt(value, 0, 64) - if err != nil { - return err - } - container.Cgroups.MemorySwap = v - return nil -} - -func addCap(container *libcontainer.Config, context interface{}, value string) error { - container.Capabilities = append(container.Capabilities, value) - return nil -} - -func dropCap(container *libcontainer.Config, context interface{}, value string) error { - // If the capability is specified multiple times, remove all instances. - for i, capability := range container.Capabilities { - if capability == value { - container.Capabilities = append(container.Capabilities[:i], container.Capabilities[i+1:]...) - } - } - - // The capability wasn't found so we will drop it anyways. - return nil -} - -func addNamespace(container *libcontainer.Config, context interface{}, value string) error { - container.Namespaces[value] = true - return nil -} - -func dropNamespace(container *libcontainer.Config, context interface{}, value string) error { - container.Namespaces[value] = false - return nil -} - -func readonlyFs(container *libcontainer.Config, context interface{}, value string) error { - switch value { - case "1", "true": - container.MountConfig.ReadonlyFs = true - default: - container.MountConfig.ReadonlyFs = false - } - return nil -} - -func joinNetNamespace(container *libcontainer.Config, context interface{}, value string) error { - var ( - running = context.(map[string]*exec.Cmd) - cmd = running[value] - ) - - if cmd == nil || cmd.Process == nil { - return fmt.Errorf("%s is not a valid running container to join", value) - } - - nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net") - container.Networks = append(container.Networks, &libcontainer.Network{ - Type: "netns", - NsPath: nspath, - }) - - return nil -} - -// configureCustomOptions takes string commands from the user and allows modification of the -// container's default configuration. -// -// TODO: this can be moved to a general utils or parser in pkg -func ParseConfiguration(container *libcontainer.Config, running map[string]*exec.Cmd, opts []string) error { - for _, opt := range opts { - kv := strings.SplitN(opt, "=", 2) - if len(kv) < 2 { - return fmt.Errorf("invalid format for %s", opt) - } - - action, exists := actions[kv[0]] - if !exists { - return fmt.Errorf("%s is not a valid option for the native driver", kv[0]) - } - - if err := action(container, running, kv[1]); err != nil { - return err - } - } - return nil -} diff --git a/daemon/execdriver/native/configuration/parse_test.go b/daemon/execdriver/native/configuration/parse_test.go deleted file mode 100644 index 1493d2b29b..0000000000 --- a/daemon/execdriver/native/configuration/parse_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package configuration - -import ( - "testing" - - "github.com/docker/docker/daemon/execdriver/native/template" - "github.com/docker/libcontainer/security/capabilities" -) - -// Checks whether the expected capability is specified in the capabilities. -func hasCapability(expected string, capabilities []string) bool { - for _, capability := range capabilities { - if capability == expected { - return true - } - } - return false -} - -func TestSetReadonlyRootFs(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "fs.readonly=true", - } - ) - - if container.MountConfig.ReadonlyFs { - t.Fatal("container should not have a readonly rootfs by default") - } - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if !container.MountConfig.ReadonlyFs { - t.Fatal("container should have a readonly rootfs") - } -} - -func TestConfigurationsDoNotConflict(t *testing.T) { - var ( - container1 = template.New() - container2 = template.New() - opts = []string{ - "cap.add=NET_ADMIN", - } - ) - - if err := ParseConfiguration(container1, nil, opts); err != nil { - t.Fatal(err) - } - - if !hasCapability("NET_ADMIN", container1.Capabilities) { - t.Fatal("container one should have NET_ADMIN enabled") - } - if hasCapability("NET_ADMIN", container2.Capabilities) { - t.Fatal("container two should not have NET_ADMIN enabled") - } -} - -func TestCpusetCpus(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "cgroups.cpuset.cpus=1,2", - } - ) - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if expected := "1,2"; container.Cgroups.CpusetCpus != expected { - t.Fatalf("expected %s got %s for cpuset.cpus", expected, container.Cgroups.CpusetCpus) - } -} - -func TestAppArmorProfile(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "apparmor_profile=koye-the-protector", - } - ) - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if expected := "koye-the-protector"; container.AppArmorProfile != expected { - t.Fatalf("expected profile %s got %s", expected, container.AppArmorProfile) - } -} - -func TestCpuShares(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "cgroups.cpu_shares=1048", - } - ) - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if expected := int64(1048); container.Cgroups.CpuShares != expected { - t.Fatalf("expected cpu shares %d got %d", expected, container.Cgroups.CpuShares) - } -} - -func TestMemory(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "cgroups.memory=500m", - } - ) - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if expected := int64(500 * 1024 * 1024); container.Cgroups.Memory != expected { - t.Fatalf("expected memory %d got %d", expected, container.Cgroups.Memory) - } -} - -func TestMemoryReservation(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "cgroups.memory_reservation=500m", - } - ) - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected { - t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation) - } -} - -func TestAddCap(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "cap.add=MKNOD", - "cap.add=SYS_ADMIN", - } - ) - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if !hasCapability("MKNOD", container.Capabilities) { - t.Fatal("container should have MKNOD enabled") - } - if !hasCapability("SYS_ADMIN", container.Capabilities) { - t.Fatal("container should have SYS_ADMIN enabled") - } -} - -func TestDropCap(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "cap.drop=MKNOD", - } - ) - // enabled all caps like in privileged mode - container.Capabilities = capabilities.GetAllCapabilities() - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if hasCapability("MKNOD", container.Capabilities) { - t.Fatal("container should not have MKNOD enabled") - } -} - -func TestDropNamespace(t *testing.T) { - var ( - container = template.New() - opts = []string{ - "ns.drop=NEWNET", - } - ) - if err := ParseConfiguration(container, nil, opts); err != nil { - t.Fatal(err) - } - - if container.Namespaces["NEWNET"] { - t.Fatal("container should not have NEWNET enabled") - } -} diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index e475a1f2ad..492247e492 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -9,7 +9,6 @@ import ( "path/filepath" "github.com/docker/docker/daemon/execdriver" - "github.com/docker/docker/daemon/execdriver/native/configuration" "github.com/docker/docker/daemon/execdriver/native/template" "github.com/docker/libcontainer" "github.com/docker/libcontainer/apparmor" @@ -23,14 +22,15 @@ import ( func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, error) { container := template.New() - container.Hostname = getEnv("HOSTNAME", c.Env) - container.Tty = c.Tty - container.User = c.User + container.Hostname = getEnv("HOSTNAME", c.ProcessConfig.Env) + container.Tty = c.ProcessConfig.Tty + container.User = c.ProcessConfig.User container.WorkingDir = c.WorkingDir - container.Env = c.Env + container.Env = c.ProcessConfig.Env container.Cgroups.Name = c.ID container.Cgroups.AllowedDevices = c.AllowedDevices container.MountConfig.DeviceNodes = c.AutoCreatedDevices + container.RootFs = c.Rootfs // check to see if we are running in ramdisk to disable pivot root container.MountConfig.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" @@ -40,7 +40,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e return nil, err } - if c.Privileged { + if c.ProcessConfig.Privileged { if err := d.setPrivileged(container); err != nil { return nil, err } @@ -50,6 +50,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e } } + if c.AppArmorProfile != "" { + container.AppArmorProfile = c.AppArmorProfile + } + if err := d.setupCgroups(container, c); err != nil { return nil, err } @@ -69,10 +73,6 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e } d.Unlock() - if err := configuration.ParseConfiguration(container, cmds, c.Config["native"]); err != nil { - return nil, err - } - return container, nil } @@ -95,6 +95,7 @@ func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Com vethNetwork := libcontainer.Network{ Mtu: c.Network.Mtu, Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), + MacAddress: c.Network.Interface.MacAddress, Gateway: c.Network.Interface.Gateway, Type: "veth", Bridge: c.Network.Interface.Bridge, @@ -161,12 +162,13 @@ func (d *driver) setupCgroups(container *libcontainer.Config, c *execdriver.Comm func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Command) error { for _, m := range c.Mounts { - container.MountConfig.Mounts = append(container.MountConfig.Mounts, mount.Mount{ + container.MountConfig.Mounts = append(container.MountConfig.Mounts, &mount.Mount{ Type: "bind", Source: m.Source, Destination: m.Destination, Writable: m.Writable, Private: m.Private, + Slave: m.Slave, }) } @@ -174,8 +176,8 @@ func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Comma } func (d *driver) setupLabels(container *libcontainer.Config, c *execdriver.Command) error { - container.ProcessLabel = c.Config["process_label"][0] - container.MountConfig.MountLabel = c.Config["mount_label"][0] + container.ProcessLabel = c.ProcessLabel + container.MountConfig.MountLabel = c.MountLabel return nil } diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index c45188b6bc..3628d7b575 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -22,6 +22,7 @@ import ( "github.com/docker/libcontainer/cgroups/systemd" consolepkg "github.com/docker/libcontainer/console" "github.com/docker/libcontainer/namespaces" + _ "github.com/docker/libcontainer/namespaces/nsenter" "github.com/docker/libcontainer/system" ) @@ -68,40 +69,40 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba var term execdriver.Terminal - if c.Tty { - term, err = NewTtyConsole(c, pipes) + if c.ProcessConfig.Tty { + term, err = NewTtyConsole(&c.ProcessConfig, pipes) } else { - term, err = execdriver.NewStdConsole(c, pipes) + term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes) } if err != nil { return -1, err } - c.Terminal = term + c.ProcessConfig.Terminal = term d.Lock() d.activeContainers[c.ID] = &activeContainer{ container: container, - cmd: &c.Cmd, + cmd: &c.ProcessConfig.Cmd, } d.Unlock() var ( dataPath = filepath.Join(d.root, c.ID) - args = append([]string{c.Entrypoint}, c.Arguments...) + args = append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...) ) if err := d.createContainerRoot(c.ID); err != nil { return -1, err } - defer d.removeContainerRoot(c.ID) + defer d.cleanContainer(c.ID) if err := d.writeContainerFile(container, c.ID); err != nil { return -1, err } - return namespaces.Exec(container, c.Stdin, c.Stdout, c.Stderr, c.Console, c.Rootfs, dataPath, args, func(container *libcontainer.Config, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd { - c.Path = d.initPath - c.Args = append([]string{ + return namespaces.Exec(container, c.ProcessConfig.Stdin, c.ProcessConfig.Stdout, c.ProcessConfig.Stderr, c.ProcessConfig.Console, dataPath, args, func(container *libcontainer.Config, console, dataPath, init string, child *os.File, args []string) *exec.Cmd { + c.ProcessConfig.Path = d.initPath + c.ProcessConfig.Args = append([]string{ DriverName, "-console", console, "-pipe", "3", @@ -110,25 +111,25 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba }, args...) // set this to nil so that when we set the clone flags anything else is reset - c.SysProcAttr = &syscall.SysProcAttr{ + c.ProcessConfig.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: uintptr(namespaces.GetNamespaceFlags(container.Namespaces)), } - c.ExtraFiles = []*os.File{child} + c.ProcessConfig.ExtraFiles = []*os.File{child} - c.Env = container.Env - c.Dir = c.Rootfs + c.ProcessConfig.Env = container.Env + c.ProcessConfig.Dir = container.RootFs - return &c.Cmd + return &c.ProcessConfig.Cmd }, func() { if startCallback != nil { - c.ContainerPid = c.Process.Pid - startCallback(c) + c.ContainerPid = c.ProcessConfig.Process.Pid + startCallback(&c.ProcessConfig, c.ContainerPid) } }) } func (d *driver) Kill(p *execdriver.Command, sig int) error { - return syscall.Kill(p.Process.Pid, syscall.Signal(sig)) + return syscall.Kill(p.ProcessConfig.Process.Pid, syscall.Signal(sig)) } func (d *driver) Pause(c *execdriver.Command) error { @@ -176,16 +177,16 @@ func (d *driver) Terminate(p *execdriver.Command) error { state = &libcontainer.State{InitStartTime: string(data)} } - currentStartTime, err := system.GetProcessStartTime(p.Process.Pid) + currentStartTime, err := system.GetProcessStartTime(p.ProcessConfig.Process.Pid) if err != nil { return err } if state.InitStartTime == currentStartTime { - err = syscall.Kill(p.Process.Pid, 9) - syscall.Wait4(p.Process.Pid, nil, 0, nil) + err = syscall.Kill(p.ProcessConfig.Process.Pid, 9) + syscall.Wait4(p.ProcessConfig.Process.Pid, nil, 0, nil) } - d.removeContainerRoot(p.ID) + d.cleanContainer(p.ID) return err @@ -226,15 +227,18 @@ func (d *driver) writeContainerFile(container *libcontainer.Config, id string) e return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655) } +func (d *driver) cleanContainer(id string) error { + d.Lock() + delete(d.activeContainers, id) + d.Unlock() + return os.RemoveAll(filepath.Join(d.root, id, "container.json")) +} + func (d *driver) createContainerRoot(id string) error { return os.MkdirAll(filepath.Join(d.root, id), 0655) } -func (d *driver) removeContainerRoot(id string) error { - d.Lock() - delete(d.activeContainers, id) - d.Unlock() - +func (d *driver) Clean(id string) error { return os.RemoveAll(filepath.Join(d.root, id)) } @@ -252,7 +256,7 @@ type TtyConsole struct { MasterPty *os.File } -func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) { +func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) { ptyMaster, console, err := consolepkg.CreateMasterAndConsole() if err != nil { return nil, err @@ -262,12 +266,12 @@ func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyCo MasterPty: ptyMaster, } - if err := tty.AttachPipes(&command.Cmd, pipes); err != nil { + if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil { tty.Close() return nil, err } - command.Console = console + processConfig.Console = console return tty, nil } diff --git a/daemon/execdriver/native/exec.go b/daemon/execdriver/native/exec.go new file mode 100644 index 0000000000..0f7e5c07bd --- /dev/null +++ b/daemon/execdriver/native/exec.go @@ -0,0 +1,70 @@ +// +build linux + +package native + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + + "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/reexec" + "github.com/docker/libcontainer" + "github.com/docker/libcontainer/namespaces" +) + +const execCommandName = "nsenter-exec" + +func init() { + reexec.Register(execCommandName, nsenterExec) +} + +func nsenterExec() { + runtime.LockOSThread() + + // User args are passed after '--' in the command line. + userArgs := findUserArgs() + + config, err := loadConfigFromFd() + if err != nil { + log.Fatalf("docker-exec: unable to receive config from sync pipe: %s", err) + } + + if err := namespaces.FinalizeSetns(config, userArgs); err != nil { + log.Fatalf("docker-exec: failed to exec: %s", err) + } +} + +// TODO(vishh): Add support for running in priviledged mode and running as a different user. +func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { + active := d.activeContainers[c.ID] + if active == nil { + return -1, fmt.Errorf("No active container exists with ID %s", c.ID) + } + state, err := libcontainer.GetState(filepath.Join(d.root, c.ID)) + if err != nil { + return -1, fmt.Errorf("State unavailable for container with ID %s. The container may have been cleaned up already. Error: %s", c.ID, err) + } + + var term execdriver.Terminal + + if processConfig.Tty { + term, err = NewTtyConsole(processConfig, pipes) + } else { + term, err = execdriver.NewStdConsole(processConfig, pipes) + } + + processConfig.Terminal = term + + args := append([]string{processConfig.Entrypoint}, processConfig.Arguments...) + + return namespaces.ExecIn(active.container, state, args, os.Args[0], "exec", processConfig.Stdin, processConfig.Stdout, processConfig.Stderr, processConfig.Console, + func(cmd *exec.Cmd) { + if startCallback != nil { + startCallback(&c.ProcessConfig, cmd.Process.Pid) + } + }) +} diff --git a/daemon/execdriver/native/utils.go b/daemon/execdriver/native/utils.go new file mode 100644 index 0000000000..e337cf4316 --- /dev/null +++ b/daemon/execdriver/native/utils.go @@ -0,0 +1,35 @@ +// +build linux + +package native + +import ( + "os" + + "github.com/docker/libcontainer" + "github.com/docker/libcontainer/syncpipe" +) + +func findUserArgs() []string { + for i, a := range os.Args { + if a == "--" { + return os.Args[i+1:] + } + } + return []string{} +} + +// loadConfigFromFd loads a container's config from the sync pipe that is provided by +// fd 3 when running a process +func loadConfigFromFd() (*libcontainer.Config, error) { + syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3) + if err != nil { + return nil, err + } + + var config *libcontainer.Config + if err := syncPipe.ReadFromParent(&config); err != nil { + return nil, err + } + + return config, nil +} diff --git a/daemon/execdriver/termconsole.go b/daemon/execdriver/termconsole.go index dc0e54ccdb..4dc18e5703 100644 --- a/daemon/execdriver/termconsole.go +++ b/daemon/execdriver/termconsole.go @@ -8,10 +8,10 @@ import ( type StdConsole struct { } -func NewStdConsole(command *Command, pipes *Pipes) (*StdConsole, error) { +func NewStdConsole(processConfig *ProcessConfig, pipes *Pipes) (*StdConsole, error) { std := &StdConsole{} - if err := std.AttachPipes(&command.Cmd, pipes); err != nil { + if err := std.AttachPipes(&processConfig.Cmd, pipes); err != nil { return nil, err } return std, nil diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index ebd4929389..8e3ae0b181 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -30,8 +30,8 @@ import ( "sync" "syscall" - "github.com/docker/docker/archive" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/log" mountpk "github.com/docker/docker/pkg/mount" "github.com/docker/docker/utils" @@ -294,23 +294,44 @@ func (a *Driver) Put(id string) { } } -// Returns an archive of the contents for the id -func (a *Driver) Diff(id string) (archive.Archive, error) { +// Diff produces an archive of the changes between the specified +// layer and its parent layer which may be "". +func (a *Driver) Diff(id, parent string) (archive.Archive, error) { + // AUFS doesn't need the parent layer to produce a diff. return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ Compression: archive.Uncompressed, }) } -func (a *Driver) ApplyDiff(id string, diff archive.ArchiveReader) error { +func (a *Driver) applyDiff(id string, diff archive.ArchiveReader) error { return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil) } -// Returns the size of the contents for the id -func (a *Driver) DiffSize(id string) (int64, error) { +// DiffSize calculates the changes between the specified id +// and its parent and returns the size in bytes of the changes +// relative to its base filesystem directory. +func (a *Driver) DiffSize(id, parent string) (bytes int64, err error) { + // AUFS doesn't need the parent layer to calculate the diff size. return utils.TreeSize(path.Join(a.rootPath(), "diff", id)) } -func (a *Driver) Changes(id string) ([]archive.Change, error) { +// ApplyDiff extracts the changeset from the given diff into the +// layer with the specified id and parent, returning the size of the +// new layer in bytes. +func (a *Driver) ApplyDiff(id, parent string, diff archive.ArchiveReader) (bytes int64, err error) { + // AUFS doesn't need the parent id to apply the diff. + if err = a.applyDiff(id, diff); err != nil { + return + } + + return a.DiffSize(id, parent) +} + +// Changes produces a list of changes between the specified layer +// and its parent layer. If parent is "", then all changes will be ADD changes. +func (a *Driver) Changes(id, parent string) ([]archive.Change, error) { + // AUFS doesn't have snapshots, so we need to get changes from all parent + // layers. layers, err := a.getParentLayerPaths(id) if err != nil { return nil, err @@ -323,9 +344,6 @@ func (a *Driver) getParentLayerPaths(id string) ([]string, error) { if err != nil { return nil, err } - if len(parentIds) == 0 { - return nil, fmt.Errorf("Dir %s does not have any parent layers", id) - } layers := make([]string, len(parentIds)) // Get the diff paths for all the parent ids diff --git a/daemon/graphdriver/aufs/aufs_test.go b/daemon/graphdriver/aufs/aufs_test.go index 081fb88984..cc5b3a2030 100644 --- a/daemon/graphdriver/aufs/aufs_test.go +++ b/daemon/graphdriver/aufs/aufs_test.go @@ -4,8 +4,8 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "github.com/docker/docker/archive" "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/pkg/archive" "io/ioutil" "os" "path" @@ -330,7 +330,7 @@ func TestGetDiff(t *testing.T) { } f.Close() - a, err := d.Diff("1") + a, err := d.Diff("1", "") if err != nil { t.Fatal(err) } @@ -374,7 +374,7 @@ func TestChanges(t *testing.T) { t.Fatal(err) } - changes, err := d.Changes("2") + changes, err := d.Changes("2", "") if err != nil { t.Fatal(err) } @@ -413,7 +413,7 @@ func TestChanges(t *testing.T) { t.Fatal(err) } - changes, err = d.Changes("3") + changes, err = d.Changes("3", "") if err != nil { t.Fatal(err) } @@ -465,7 +465,7 @@ func TestDiffSize(t *testing.T) { t.Fatal(err) } - diffSize, err := d.DiffSize("1") + diffSize, err := d.DiffSize("1", "") if err != nil { t.Fatal(err) } @@ -507,7 +507,7 @@ func TestChildDiffSize(t *testing.T) { t.Fatal(err) } - diffSize, err := d.DiffSize("1") + diffSize, err := d.DiffSize("1", "") if err != nil { t.Fatal(err) } @@ -519,7 +519,7 @@ func TestChildDiffSize(t *testing.T) { t.Fatal(err) } - diffSize, err = d.DiffSize("2") + diffSize, err = d.DiffSize("2", "") if err != nil { t.Fatal(err) } @@ -602,7 +602,7 @@ func TestApplyDiff(t *testing.T) { } f.Close() - diff, err := d.Diff("1") + diff, err := d.Diff("1", "") if err != nil { t.Fatal(err) } @@ -614,7 +614,7 @@ func TestApplyDiff(t *testing.T) { t.Fatal(err) } - if err := d.ApplyDiff("3", diff); err != nil { + if err := d.applyDiff("3", diff); err != nil { t.Fatal(err) } diff --git a/daemon/graphdriver/btrfs/btrfs.go b/daemon/graphdriver/btrfs/btrfs.go index c491fd7908..26102aa1ef 100644 --- a/daemon/graphdriver/btrfs/btrfs.go +++ b/daemon/graphdriver/btrfs/btrfs.go @@ -44,9 +44,11 @@ func Init(home string, options []string) (graphdriver.Driver, error) { return nil, err } - return &Driver{ + driver := &Driver{ home: home, - }, nil + } + + return graphdriver.NaiveDiffDriver(driver), nil } type Driver struct { diff --git a/daemon/graphdriver/devmapper/attach_loopback.go b/daemon/graphdriver/devmapper/attach_loopback.go index 86714d1959..9cfa18a4d3 100644 --- a/daemon/graphdriver/devmapper/attach_loopback.go +++ b/daemon/graphdriver/devmapper/attach_loopback.go @@ -52,7 +52,7 @@ func openNextAvailableLoopback(index int, sparseFile *os.File) (loopFile *os.Fil // OpenFile adds O_CLOEXEC loopFile, err = os.OpenFile(target, os.O_RDWR, 0644) if err != nil { - log.Errorf("Error openning loopback device: %s", err) + log.Errorf("Error opening loopback device: %s", err) return nil, ErrAttachLoopbackDevice } @@ -97,7 +97,7 @@ func attachLoopDevice(sparseName string) (loop *os.File, err error) { // OpenFile adds O_CLOEXEC sparseFile, err := os.OpenFile(sparseName, os.O_RDWR, 0644) if err != nil { - log.Errorf("Error openning sparse file %s: %s", sparseName, err) + log.Errorf("Error opening sparse file %s: %s", sparseName, err) return nil, ErrAttachLoopbackDevice } defer sparseFile.Close() diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 42315c6e82..ccaea0181e 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -306,6 +306,10 @@ func (devices *DeviceSet) createFilesystem(info *DevInfo) error { if err != nil { err = exec.Command("mkfs.ext4", append([]string{"-E", "nodiscard,lazy_itable_init=0"}, args...)...).Run() } + if err != nil { + return err + } + err = exec.Command("tune2fs", append([]string{"-c", "-1", "-i", "0"}, devname)...).Run() default: err = fmt.Errorf("Unsupported filesystem type %s", devices.filesystem) } @@ -462,7 +466,13 @@ func minor(device uint64) uint64 { func (devices *DeviceSet) ResizePool(size int64) error { dirname := devices.loopbackDir() datafilename := path.Join(dirname, "data") + if len(devices.dataDevice) > 0 { + datafilename = devices.dataDevice + } metadatafilename := path.Join(dirname, "metadata") + if len(devices.metadataDevice) > 0 { + metadatafilename = devices.metadataDevice + } datafile, err := os.OpenFile(datafilename, os.O_RDWR, 0) if datafile == nil { @@ -832,7 +842,7 @@ func (devices *DeviceSet) waitRemove(devname string) error { log.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname) defer log.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname) i := 0 - for ; i < 1000; i += 1 { + for ; i < 1000; i++ { devinfo, err := getInfo(devname) if err != nil { // If there is an error we assume the device doesn't exist. @@ -861,7 +871,7 @@ func (devices *DeviceSet) waitRemove(devname string) error { // or b) the 10 second timeout expires. func (devices *DeviceSet) waitClose(info *DevInfo) error { i := 0 - for ; i < 1000; i += 1 { + for ; i < 1000; i++ { devinfo, err := getInfo(info.Name()) if err != nil { return err @@ -1130,8 +1140,16 @@ func (devices *DeviceSet) Status() *Status { status := &Status{} status.PoolName = devices.getPoolName() - status.DataLoopback = path.Join(devices.loopbackDir(), "data") - status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") + if len(devices.dataDevice) > 0 { + status.DataLoopback = devices.dataDevice + } else { + status.DataLoopback = path.Join(devices.loopbackDir(), "data") + } + if len(devices.metadataDevice) > 0 { + status.MetadataLoopback = devices.metadataDevice + } else { + status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata") + } totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() if err == nil { diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index 4c13eb0421..8f9de85d4e 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/log" "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/units" ) func init() { @@ -42,7 +43,7 @@ func Init(home string, options []string) (graphdriver.Driver, error) { home: home, } - return d, nil + return graphdriver.NaiveDiffDriver(d), nil } func (d *Driver) String() string { @@ -54,13 +55,16 @@ func (d *Driver) Status() [][2]string { status := [][2]string{ {"Pool Name", s.PoolName}, - {"Pool Blocksize", fmt.Sprintf("%d Kb", s.SectorSize/1024)}, + {"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(int64(s.SectorSize)))}, {"Data file", s.DataLoopback}, {"Metadata file", s.MetadataLoopback}, - {"Data Space Used", fmt.Sprintf("%.1f Mb", float64(s.Data.Used)/(1024*1024))}, - {"Data Space Total", fmt.Sprintf("%.1f Mb", float64(s.Data.Total)/(1024*1024))}, - {"Metadata Space Used", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Used)/(1024*1024))}, - {"Metadata Space Total", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Total)/(1024*1024))}, + {"Data Space Used", fmt.Sprintf("%s", units.HumanSize(int64(s.Data.Used)))}, + {"Data Space Total", fmt.Sprintf("%s", units.HumanSize(int64(s.Data.Total)))}, + {"Metadata Space Used", fmt.Sprintf("%s", units.HumanSize(int64(s.Metadata.Used)))}, + {"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(int64(s.Metadata.Total)))}, + } + if vStr, err := GetLibraryVersion(); err == nil { + status = append(status, [2]string{"Library Version", vStr}) } return status } diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go index 90ed1d8162..91040db97a 100644 --- a/daemon/graphdriver/driver.go +++ b/daemon/graphdriver/driver.go @@ -6,7 +6,7 @@ import ( "os" "path" - "github.com/docker/docker/archive" + "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/mount" ) @@ -19,26 +19,56 @@ const ( type InitFunc func(root string, options []string) (Driver, error) -type Driver interface { +// ProtoDriver defines the basic capabilities of a driver. +// This interface exists solely to be a minimum set of methods +// for client code which choose not to implement the entire Driver +// interface and use the NaiveDiffDriver wrapper constructor. +// +// Use of ProtoDriver directly by client code is not recommended. +type ProtoDriver interface { + // String returns a string representation of this driver. String() string - + // Create creates a new, empty, filesystem layer with the + // specified id and parent. Parent may be "". Create(id, parent string) error + // Remove attempts to remove the filesystem layer with this id. Remove(id string) error - + // Get returns the mountpoint for the layered filesystem referred + // to by this id. You can optionally specify a mountLabel or "". + // Returns the absolute path to the mounted layered filesystem. Get(id, mountLabel string) (dir string, err error) + // Put releases the system resources for the specified id, + // e.g, unmounting layered filesystem. Put(id string) + // Exists returns whether a filesystem layer with the specified + // ID exists on this driver. Exists(id string) bool - + // Status returns a set of key-value pairs which give low + // level diagnostic status about this driver. Status() [][2]string - + // Cleanup performs necessary tasks to release resources + // held by the driver, e.g., unmounting all layered filesystems + // known to this driver. Cleanup() error } -type Differ interface { - Diff(id string) (archive.Archive, error) - Changes(id string) ([]archive.Change, error) - ApplyDiff(id string, diff archive.ArchiveReader) error - DiffSize(id string) (bytes int64, err error) +// Driver is the interface for layered/snapshot file system drivers. +type Driver interface { + ProtoDriver + // Diff produces an archive of the changes between the specified + // layer and its parent layer which may be "". + Diff(id, parent string) (archive.Archive, error) + // Changes produces a list of changes between the specified layer + // and its parent layer. If parent is "", then all changes will be ADD changes. + Changes(id, parent string) ([]archive.Change, error) + // ApplyDiff extracts the changeset from the given diff into the + // layer with the specified id and parent, returning the size of the + // new layer in bytes. + ApplyDiff(id, parent string, diff archive.ArchiveReader) (bytes int64, err error) + // DiffSize calculates the changes between the specified id + // and its parent and returns the size in bytes of the changes + // relative to its base filesystem directory. + DiffSize(id, parent string) (bytes int64, err error) } var ( diff --git a/daemon/graphdriver/fsdiff.go b/daemon/graphdriver/fsdiff.go new file mode 100644 index 0000000000..5e9d32c1c8 --- /dev/null +++ b/daemon/graphdriver/fsdiff.go @@ -0,0 +1,165 @@ +package graphdriver + +import ( + "fmt" + "time" + + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/log" + "github.com/docker/docker/utils" +) + +// naiveDiffDriver takes a ProtoDriver and adds the +// capability of the Diffing methods which it may or may not +// support on its own. See the comment on the exported +// NaiveDiffDriver function below. +// Notably, the AUFS driver doesn't need to be wrapped like this. +type naiveDiffDriver struct { + ProtoDriver +} + +// NaiveDiffDriver returns a fully functional driver that wraps the +// given ProtoDriver and adds the capability of the following methods which +// it may or may not support on its own: +// Diff(id, parent string) (archive.Archive, error) +// Changes(id, parent string) ([]archive.Change, error) +// ApplyDiff(id, parent string, diff archive.ArchiveReader) (bytes int64, err error) +// DiffSize(id, parent string) (bytes int64, err error) +func NaiveDiffDriver(driver ProtoDriver) Driver { + return &naiveDiffDriver{ProtoDriver: driver} +} + +// Diff produces an archive of the changes between the specified +// layer and its parent layer which may be "". +func (gdw *naiveDiffDriver) Diff(id, parent string) (arch archive.Archive, err error) { + driver := gdw.ProtoDriver + + layerFs, err := driver.Get(id, "") + if err != nil { + return nil, err + } + + defer func() { + if err != nil { + driver.Put(id) + } + }() + + if parent == "" { + archive, err := archive.Tar(layerFs, archive.Uncompressed) + if err != nil { + return nil, err + } + return ioutils.NewReadCloserWrapper(archive, func() error { + err := archive.Close() + driver.Put(id) + return err + }), nil + } + + parentFs, err := driver.Get(parent, "") + if err != nil { + return nil, err + } + defer driver.Put(parent) + + changes, err := archive.ChangesDirs(layerFs, parentFs) + if err != nil { + return nil, err + } + + archive, err := archive.ExportChanges(layerFs, changes) + if err != nil { + return nil, err + } + + return ioutils.NewReadCloserWrapper(archive, func() error { + err := archive.Close() + driver.Put(id) + return err + }), nil +} + +// Changes produces a list of changes between the specified layer +// and its parent layer. If parent is "", then all changes will be ADD changes. +func (gdw *naiveDiffDriver) Changes(id, parent string) ([]archive.Change, error) { + driver := gdw.ProtoDriver + + layerFs, err := driver.Get(id, "") + if err != nil { + return nil, err + } + defer driver.Put(id) + + parentFs := "" + + if parent != "" { + parentFs, err = driver.Get(parent, "") + if err != nil { + return nil, err + } + defer driver.Put(parent) + } + + return archive.ChangesDirs(layerFs, parentFs) +} + +// ApplyDiff extracts the changeset from the given diff into the +// layer with the specified id and parent, returning the size of the +// new layer in bytes. +func (gdw *naiveDiffDriver) ApplyDiff(id, parent string, diff archive.ArchiveReader) (bytes int64, err error) { + driver := gdw.ProtoDriver + + // Mount the root filesystem so we can apply the diff/layer. + layerFs, err := driver.Get(id, "") + if err != nil { + return + } + defer driver.Put(id) + + start := time.Now().UTC() + log.Debugf("Start untar layer") + if err = archive.ApplyLayer(layerFs, diff); err != nil { + return + } + log.Debugf("Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) + + if parent == "" { + return utils.TreeSize(layerFs) + } + + parentFs, err := driver.Get(parent, "") + if err != nil { + err = fmt.Errorf("Driver %s failed to get image parent %s: %s", driver, parent, err) + return + } + defer driver.Put(parent) + + changes, err := archive.ChangesDirs(layerFs, parentFs) + if err != nil { + return + } + + return archive.ChangesSize(layerFs, changes), nil +} + +// DiffSize calculates the changes between the specified layer +// and its parent and returns the size in bytes of the changes +// relative to its base filesystem directory. +func (gdw *naiveDiffDriver) DiffSize(id, parent string) (bytes int64, err error) { + driver := gdw.ProtoDriver + + changes, err := gdw.Changes(id, parent) + if err != nil { + return + } + + layerFs, err := driver.Get(id, "") + if err != nil { + return + } + defer driver.Put(id) + + return archive.ChangesSize(layerFs, changes), nil +} diff --git a/daemon/graphdriver/vfs/driver.go b/daemon/graphdriver/vfs/driver.go index 2ea6325a1e..a186060d03 100644 --- a/daemon/graphdriver/vfs/driver.go +++ b/daemon/graphdriver/vfs/driver.go @@ -3,10 +3,12 @@ package vfs import ( "bytes" "fmt" - "github.com/docker/docker/daemon/graphdriver" "os" "os/exec" "path" + + "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/libcontainer/label" ) func init() { @@ -17,7 +19,7 @@ func Init(home string, options []string) (graphdriver.Driver, error) { d := &Driver{ home: home, } - return d, nil + return graphdriver.NaiveDiffDriver(d), nil } type Driver struct { @@ -67,6 +69,10 @@ func (d *Driver) Create(id, parent string) error { if err := os.Mkdir(dir, 0755); err != nil { return err } + opts := []string{"level:s0"} + if _, mountLabel, err := label.InitLabels(opts); err == nil { + label.Relabel(dir, mountLabel, "") + } if parent == "" { return nil } diff --git a/daemon/image_delete.go b/daemon/image_delete.go index 77e8f85907..332db7b4c0 100644 --- a/daemon/image_delete.go +++ b/daemon/image_delete.go @@ -33,7 +33,6 @@ func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine. var ( repoName, tag string tags = []string{} - tagDeleted bool ) // FIXME: please respect DRY and centralize repo+tag parsing in a single central place! -- shykes @@ -60,9 +59,11 @@ func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine. return err } + repos := daemon.Repositories().ByID()[img.ID] + //If delete by id, see if the id belong only to one repository if repoName == "" { - for _, repoAndTag := range daemon.Repositories().ByID()[img.ID] { + for _, repoAndTag := range repos { parsedRepo, parsedTag := parsers.ParseRepositoryTag(repoAndTag) if repoName == "" || repoName == parsedRepo { repoName = parsedRepo @@ -83,9 +84,15 @@ func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine. return nil } - //Untag the current image + if len(repos) <= 1 { + if err := daemon.canDeleteImage(img.ID, force); err != nil { + return err + } + } + + // Untag the current image for _, tag := range tags { - tagDeleted, err = daemon.Repositories().Delete(repoName, tag) + tagDeleted, err := daemon.Repositories().Delete(repoName, tag) if err != nil { return err } @@ -99,9 +106,6 @@ func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine. tags = daemon.Repositories().ByID()[img.ID] if (len(tags) <= 1 && repoName == "") || len(tags) == 0 { if len(byParents[img.ID]) == 0 { - if err := daemon.canDeleteImage(img.ID, force, tagDeleted); err != nil { - return err - } if err := daemon.Repositories().DeleteAll(img.ID); err != nil { return err } @@ -125,11 +129,7 @@ func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine. return nil } -func (daemon *Daemon) canDeleteImage(imgID string, force, untagged bool) error { - var message string - if untagged { - message = " (docker untagged the image)" - } +func (daemon *Daemon) canDeleteImage(imgID string, force bool) error { for _, container := range daemon.List() { parent, err := daemon.Repositories().LookupImage(container.Image) if err != nil { @@ -138,13 +138,13 @@ func (daemon *Daemon) canDeleteImage(imgID string, force, untagged bool) error { if err := parent.WalkHistory(func(p *image.Image) error { if imgID == p.ID { - if container.State.IsRunning() { + if container.IsRunning() { if force { - return fmt.Errorf("Conflict, cannot force delete %s because the running container %s is using it%s, stop it and retry", utils.TruncateID(imgID), utils.TruncateID(container.ID), message) + return fmt.Errorf("Conflict, cannot force delete %s because the running container %s is using it, stop it and retry", utils.TruncateID(imgID), utils.TruncateID(container.ID)) } - return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it%s, stop it and use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID), message) + return fmt.Errorf("Conflict, cannot delete %s because the running container %s is using it, stop it and use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID)) } else if !force { - return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it%s, use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID), message) + return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it, use -f to force", utils.TruncateID(imgID), utils.TruncateID(container.ID)) } } return nil diff --git a/daemon/inspect.go b/daemon/inspect.go index 373b43b8b6..396ca0227f 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -31,7 +31,7 @@ func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status { out := &engine.Env{} out.Set("Id", container.ID) out.SetAuto("Created", container.Created) - out.Set("Path", container.Path) + out.SetJson("Path", container.Path) out.SetList("Args", container.Args) out.SetJson("Config", container.Config) out.SetJson("State", container.State) diff --git a/daemon/list.go b/daemon/list.go index 2da5254866..35effa4344 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -28,6 +28,7 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { size = job.GetenvBool("size") psFilters filters.Args filt_exited []int + filt_status []string ) outs := engine.NewTable("Created", 0) @@ -45,6 +46,8 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { } } + filt_status, _ = psFilters["status"] + names := map[string][]string{} daemon.ContainerGraph().Walk("/", func(p string, e *graphdb.Entity) error { names[e.ID()] = append(names[e.ID()], p) @@ -70,7 +73,7 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { writeCont := func(container *Container) error { container.Lock() defer container.Unlock() - if !container.State.IsRunning() && !all && n <= 0 && since == "" && before == "" { + if !container.Running && !all && n <= 0 && since == "" && before == "" { return nil } if before != "" && !foundBefore { @@ -87,10 +90,10 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { return errLast } } - if len(filt_exited) > 0 && !container.State.IsRunning() { + if len(filt_exited) > 0 && !container.Running { should_skip := true for _, code := range filt_exited { - if code == container.State.GetExitCode() { + if code == container.GetExitCode() { should_skip = false break } @@ -99,6 +102,11 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status { return nil } } + for _, status := range filt_status { + if container.State.StateString() != strings.ToLower(status) { + return nil + } + } displayed++ out := &engine.Env{} out.Set("Id", container.ID) diff --git a/daemon/logs.go b/daemon/logs.go index 386d9c69c3..b4df401efd 100644 --- a/daemon/logs.go +++ b/daemon/logs.go @@ -7,13 +7,12 @@ import ( "io" "os" "strconv" - "time" - - "github.com/docker/docker/pkg/log" - "github.com/docker/docker/pkg/tailfile" "github.com/docker/docker/engine" "github.com/docker/docker/pkg/jsonlog" + "github.com/docker/docker/pkg/log" + "github.com/docker/docker/pkg/tailfile" + "github.com/docker/docker/pkg/timeutils" ) func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status { @@ -35,7 +34,7 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status { return job.Errorf("You must choose at least one stream") } if times { - format = time.RFC3339Nano + format = timeutils.RFC3339NanoFixed } if tail == "" { tail = "all" @@ -89,9 +88,8 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status { cLog = tmp } dec := json.NewDecoder(cLog) + l := &jsonlog.JSONLog{} for { - l := &jsonlog.JSONLog{} - if err := dec.Decode(l); err == io.EOF { break } else if err != nil { @@ -103,24 +101,27 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status { logLine = fmt.Sprintf("%s %s", l.Created.Format(format), logLine) } if l.Stream == "stdout" && stdout { - fmt.Fprintf(job.Stdout, "%s", logLine) + io.WriteString(job.Stdout, logLine) } if l.Stream == "stderr" && stderr { - fmt.Fprintf(job.Stderr, "%s", logLine) + io.WriteString(job.Stderr, logLine) } + l.Reset() } } } - if follow { + if follow && container.IsRunning() { errors := make(chan error, 2) if stdout { stdoutPipe := container.StdoutLogPipe() + defer stdoutPipe.Close() go func() { errors <- jsonlog.WriteLog(stdoutPipe, job.Stdout, format) }() } if stderr { stderrPipe := container.StderrLogPipe() + defer stderrPipe.Close() go func() { errors <- jsonlog.WriteLog(stderrPipe, job.Stderr, format) }() diff --git a/daemon/monitor.go b/daemon/monitor.go index 1a929656fa..b5dd741012 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -101,15 +101,16 @@ func (m *containerMonitor) Start() error { var ( err error exitStatus int + // this variable indicates where we in execution flow: + // before Run or after + afterRun bool ) - // this variable indicates that we under container.Lock - underLock := true - // ensure that when the monitor finally exits we release the networking and unmount the rootfs defer func() { - if !underLock { + if afterRun { m.container.Lock() + m.container.setStopped(exitStatus) defer m.container.Unlock() } m.Close() @@ -122,7 +123,7 @@ func (m *containerMonitor) Start() error { m.container.RestartCount++ if err := m.container.startLoggingToDisk(); err != nil { - m.resetContainer() + m.resetContainer(false) return err } @@ -137,7 +138,7 @@ func (m *containerMonitor) Start() error { // if we receive an internal error from the initial start of a container then lets // return it instead of entering the restart loop if m.container.RestartCount == 0 { - m.resetContainer() + m.resetContainer(false) return err } @@ -146,16 +147,14 @@ func (m *containerMonitor) Start() error { } // here container.Lock is already lost - underLock = false + afterRun = true m.resetMonitor(err == nil && exitStatus == 0) if m.shouldRestart(exitStatus) { - m.container.State.SetRestarting(exitStatus) - + m.container.SetRestarting(exitStatus) m.container.LogEvent("die") - - m.resetContainer() + m.resetContainer(true) // sleep with a small time increment between each restart to help avoid issues cased by quickly // restarting the container because of some types of errors ( networking cut out, etc... ) @@ -164,24 +163,14 @@ func (m *containerMonitor) Start() error { // we need to check this before reentering the loop because the waitForNextRestart could have // been terminated by a request from a user if m.shouldStop { - m.container.State.SetStopped(exitStatus) - return err } - continue } - - m.container.State.SetStopped(exitStatus) - m.container.LogEvent("die") - - m.resetContainer() - - break + m.resetContainer(true) + return err } - - return err } // resetMonitor resets the stateful fields on the containerMonitor based on the @@ -244,17 +233,17 @@ func (m *containerMonitor) shouldRestart(exitStatus int) bool { // callback ensures that the container's state is properly updated after we // received ack from the execution drivers -func (m *containerMonitor) callback(command *execdriver.Command) { - if command.Tty { +func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid int) { + if processConfig.Tty { // The callback is called after the process Start() - // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace + // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave // which we close here. - if c, ok := command.Stdout.(io.Closer); ok { + if c, ok := processConfig.Stdout.(io.Closer); ok { c.Close() } } - m.container.State.SetRunning(command.Pid()) + m.container.setRunning(pid) // signal that the process has started // close channel only if not closed @@ -271,8 +260,13 @@ func (m *containerMonitor) callback(command *execdriver.Command) { // resetContainer resets the container's IO and ensures that the command is able to be executed again // by copying the data into a new struct -func (m *containerMonitor) resetContainer() { +// if lock is true, then container locked during reset +func (m *containerMonitor) resetContainer(lock bool) { container := m.container + if lock { + container.Lock() + defer container.Unlock() + } if container.Config.OpenStdin { if err := container.stdin.Close(); err != nil { @@ -288,8 +282,8 @@ func (m *containerMonitor) resetContainer() { log.Errorf("%s: Error close stderr: %s", container.ID, err) } - if container.command != nil && container.command.Terminal != nil { - if err := container.command.Terminal.Close(); err != nil { + if container.command != nil && container.command.ProcessConfig.Terminal != nil { + if err := container.command.ProcessConfig.Terminal.Close(); err != nil { log.Errorf("%s: Error closing terminal: %s", container.ID, err) } } @@ -299,9 +293,9 @@ func (m *containerMonitor) resetContainer() { container.stdin, container.stdinPipe = io.Pipe() } - c := container.command.Cmd + c := container.command.ProcessConfig.Cmd - container.command.Cmd = exec.Cmd{ + container.command.ProcessConfig.Cmd = exec.Cmd{ Stdin: c.Stdin, Stdout: c.Stdout, Stderr: c.Stderr, diff --git a/daemon/network_settings.go b/daemon/network_settings.go index bf28ca1b50..69c15be3db 100644 --- a/daemon/network_settings.go +++ b/daemon/network_settings.go @@ -11,6 +11,7 @@ type PortMapping map[string]string // Deprecated type NetworkSettings struct { IPAddress string IPPrefixLen int + MacAddress string Gateway string Bridge string PortMapping map[string]PortMapping // Deprecated diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index 06cf37e79f..e05a2c21a5 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -81,8 +81,10 @@ func InitDriver(job *engine.Job) engine.Status { network *net.IPNet enableIPTables = job.GetenvBool("EnableIptables") icc = job.GetenvBool("InterContainerCommunication") + ipMasq = job.GetenvBool("EnableIpMasq") ipForward = job.GetenvBool("EnableIpForward") bridgeIP = job.Getenv("BridgeIP") + fixedCIDR = job.Getenv("FixedCIDR") ) if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" { @@ -100,16 +102,13 @@ func InitDriver(job *engine.Job) engine.Status { if err != nil { // If we're not using the default bridge, fail without trying to create it if !usingDefaultBridge { - job.Logf("bridge not found: %s", bridgeIface) return job.Error(err) } // If the iface is not found, try to create it - job.Logf("creating new bridge for %s", bridgeIface) if err := createBridge(bridgeIP); err != nil { return job.Error(err) } - job.Logf("getting iface addr") addr, err = networkdriver.GetIfaceAddr(bridgeIface) if err != nil { return job.Error(err) @@ -131,7 +130,7 @@ func InitDriver(job *engine.Job) engine.Status { // Configure iptables for link support if enableIPTables { - if err := setupIPTables(addr, icc); err != nil { + if err := setupIPTables(addr, icc, ipMasq); err != nil { return job.Error(err) } } @@ -157,6 +156,16 @@ func InitDriver(job *engine.Job) engine.Status { } bridgeNetwork = network + if fixedCIDR != "" { + _, subnet, err := net.ParseCIDR(fixedCIDR) + if err != nil { + return job.Error(err) + } + log.Debugf("Subnet: %v", subnet) + if err := ipallocator.RegisterSubnet(bridgeNetwork, subnet); err != nil { + return job.Error(err) + } + } // https://github.com/docker/docker/issues/2768 job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeNetwork.IP) @@ -174,15 +183,18 @@ func InitDriver(job *engine.Job) engine.Status { return engine.StatusOK } -func setupIPTables(addr net.Addr, icc bool) error { +func setupIPTables(addr net.Addr, icc, ipmasq bool) error { // Enable NAT - natArgs := []string{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"} - if !iptables.Exists(natArgs...) { - if output, err := iptables.Raw(append([]string{"-I"}, natArgs...)...); err != nil { - return fmt.Errorf("Unable to enable network bridge NAT: %s", err) - } else if len(output) != 0 { - return fmt.Errorf("Error iptables postrouting: %s", output) + if ipmasq { + natArgs := []string{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"} + + if !iptables.Exists(natArgs...) { + if output, err := iptables.Raw(append([]string{"-I"}, natArgs...)...); err != nil { + return fmt.Errorf("Unable to enable network bridge NAT: %s", err) + } else if len(output) != 0 { + return fmt.Errorf("Error iptables postrouting: %s", output) + } } } @@ -314,17 +326,43 @@ func createBridgeIface(name string) error { return netlink.CreateBridge(name, setBridgeMacAddr) } +// Generate a IEEE802 compliant MAC address from the given IP address. +// +// The generator is guaranteed to be consistent: the same IP will always yield the same +// MAC address. This is to avoid ARP cache issues. +func generateMacAddr(ip net.IP) net.HardwareAddr { + hw := make(net.HardwareAddr, 6) + + // The first byte of the MAC address has to comply with these rules: + // 1. Unicast: Set the least-significant bit to 0. + // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. + // 3. As "small" as possible: The veth address has to be "smaller" than the bridge address. + hw[0] = 0x02 + + // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). + // Since this address is locally administered, we can do whatever we want as long as + // it doesn't conflict with other addresses. + hw[1] = 0x42 + + // Insert the IP address into the last 32 bits of the MAC address. + // This is a simple way to guarantee the address will be consistent and unique. + copy(hw[2:], ip.To4()) + + return hw +} + // Allocate a network interface func Allocate(job *engine.Job) engine.Status { var ( - ip *net.IP + ip net.IP + mac net.HardwareAddr err error id = job.Args[0] requestedIP = net.ParseIP(job.Getenv("RequestedIP")) ) if requestedIP != nil { - ip, err = ipallocator.RequestIP(bridgeNetwork, &requestedIP) + ip, err = ipallocator.RequestIP(bridgeNetwork, requestedIP) } else { ip, err = ipallocator.RequestIP(bridgeNetwork, nil) } @@ -332,17 +370,23 @@ func Allocate(job *engine.Job) engine.Status { return job.Error(err) } + // If no explicit mac address was given, generate a random one. + if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil { + mac = generateMacAddr(ip) + } + out := engine.Env{} out.Set("IP", ip.String()) out.Set("Mask", bridgeNetwork.Mask.String()) out.Set("Gateway", bridgeNetwork.IP.String()) + out.Set("MacAddress", mac.String()) out.Set("Bridge", bridgeIface) size, _ := bridgeNetwork.Mask.Size() out.SetInt("IPPrefixLen", size) currentInterfaces.Set(id, &networkInterface{ - IP: *ip, + IP: ip, }) out.WriteTo(job.Stdout) @@ -367,7 +411,7 @@ func Release(job *engine.Job) engine.Status { } } - if err := ipallocator.ReleaseIP(bridgeNetwork, &containerInterface.IP); err != nil { + if err := ipallocator.ReleaseIP(bridgeNetwork, containerInterface.IP); err != nil { log.Infof("Unable to release ip %s", err) } return engine.StatusOK @@ -389,6 +433,9 @@ func AllocatePort(job *engine.Job) engine.Status { if hostIP != "" { ip = net.ParseIP(hostIP) + if ip == nil { + return job.Errorf("Bad parameter: invalid host ip %s", hostIP) + } } // host ip, proto, and host port diff --git a/daemon/networkdriver/bridge/driver_test.go b/daemon/networkdriver/bridge/driver_test.go index 9bc6c32eb4..1bda2f4372 100644 --- a/daemon/networkdriver/bridge/driver_test.go +++ b/daemon/networkdriver/bridge/driver_test.go @@ -39,6 +39,17 @@ func newPortAllocationJob(eng *engine.Engine, port int) (job *engine.Job) { return } +func newPortAllocationJobWithInvalidHostIP(eng *engine.Engine, port int) (job *engine.Job) { + strPort := strconv.Itoa(port) + + job = eng.Job("allocate_port", "container_id") + job.Setenv("HostIP", "localhost") + job.Setenv("HostPort", strPort) + job.Setenv("Proto", "tcp") + job.Setenv("ContainerPort", strPort) + return +} + func TestAllocatePortDetection(t *testing.T) { eng := engine.New() eng.Logging = false @@ -66,3 +77,44 @@ func TestAllocatePortDetection(t *testing.T) { t.Fatal("Duplicate port allocation granted by AllocatePort") } } + +func TestHostnameFormatChecking(t *testing.T) { + eng := engine.New() + eng.Logging = false + + freePort := findFreePort(t) + + // Init driver + job := eng.Job("initdriver") + if res := InitDriver(job); res != engine.StatusOK { + t.Fatal("Failed to initialize network driver") + } + + // Allocate interface + job = eng.Job("allocate_interface", "container_id") + if res := Allocate(job); res != engine.StatusOK { + t.Fatal("Failed to allocate network interface") + } + + // Allocate port with invalid HostIP, expect failure with Bad Request http status + job = newPortAllocationJobWithInvalidHostIP(eng, freePort) + if res := AllocatePort(job); res == engine.StatusOK { + t.Fatal("Failed to check invalid HostIP") + } +} + +func TestMacAddrGeneration(t *testing.T) { + ip := net.ParseIP("192.168.0.1") + mac := generateMacAddr(ip).String() + + // Should be consistent. + if generateMacAddr(ip).String() != mac { + t.Fatal("Inconsistent MAC address") + } + + // Should be unique. + ip2 := net.ParseIP("192.168.0.2") + if generateMacAddr(ip2).String() == mac { + t.Fatal("Non-unique MAC address") + } +} diff --git a/daemon/networkdriver/ipallocator/allocator.go b/daemon/networkdriver/ipallocator/allocator.go index 1bf8e1da9b..a1aaabbdfe 100644 --- a/daemon/networkdriver/ipallocator/allocator.go +++ b/daemon/networkdriver/ipallocator/allocator.go @@ -3,26 +3,40 @@ package ipallocator import ( "encoding/binary" "errors" - "github.com/docker/docker/daemon/networkdriver" "net" "sync" + + "github.com/docker/docker/daemon/networkdriver" ) // allocatedMap is thread-unsafe set of allocated IP type allocatedMap struct { - p map[int32]struct{} - last int32 + p map[uint32]struct{} + last uint32 + begin uint32 + end uint32 } -func newAllocatedMap() *allocatedMap { - return &allocatedMap{p: make(map[int32]struct{})} +func newAllocatedMap(network *net.IPNet) *allocatedMap { + firstIP, lastIP := networkdriver.NetworkRange(network) + begin := ipToInt(firstIP) + 2 + end := ipToInt(lastIP) - 1 + return &allocatedMap{ + p: make(map[uint32]struct{}), + begin: begin, + end: end, + last: begin - 1, // so first allocated will be begin + } } type networkSet map[string]*allocatedMap var ( - ErrNoAvailableIPs = errors.New("no available ip addresses on network") - ErrIPAlreadyAllocated = errors.New("ip already allocated") + ErrNoAvailableIPs = errors.New("no available ip addresses on network") + ErrIPAlreadyAllocated = errors.New("ip already allocated") + ErrIPOutOfRange = errors.New("requested ip is out of range") + ErrNetworkAlreadyRegistered = errors.New("network already registered") + ErrBadSubnet = errors.New("network does not contain specified subnet") ) var ( @@ -30,98 +44,107 @@ var ( allocatedIPs = networkSet{} ) +// RegisterSubnet registers network in global allocator with bounds +// defined by subnet. If you want to use network range you must call +// this method before first RequestIP, otherwise full network range will be used +func RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error { + lock.Lock() + defer lock.Unlock() + key := network.String() + if _, ok := allocatedIPs[key]; ok { + return ErrNetworkAlreadyRegistered + } + n := newAllocatedMap(network) + beginIP, endIP := networkdriver.NetworkRange(subnet) + begin, end := ipToInt(beginIP)+1, ipToInt(endIP)-1 + if !(begin >= n.begin && end <= n.end && begin < end) { + return ErrBadSubnet + } + n.begin = begin + n.end = end + n.last = begin - 1 + allocatedIPs[key] = n + return nil +} + // RequestIP requests an available ip from the given network. It // will return the next available ip if the ip provided is nil. If the // ip provided is not nil it will validate that the provided ip is available // for use or return an error -func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { +func RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) { lock.Lock() defer lock.Unlock() key := network.String() allocated, ok := allocatedIPs[key] if !ok { - allocated = newAllocatedMap() + allocated = newAllocatedMap(network) allocatedIPs[key] = allocated } if ip == nil { - return allocated.getNextIP(network) + return allocated.getNextIP() } - return allocated.checkIP(network, ip) + return allocated.checkIP(ip) } // ReleaseIP adds the provided ip back into the pool of // available ips to be returned for use. -func ReleaseIP(network *net.IPNet, ip *net.IP) error { +func ReleaseIP(network *net.IPNet, ip net.IP) error { lock.Lock() defer lock.Unlock() if allocated, exists := allocatedIPs[network.String()]; exists { - pos := getPosition(network, ip) + pos := ipToInt(ip) delete(allocated.p, pos) } return nil } -// convert the ip into the position in the subnet. Only -// position are saved in the set -func getPosition(network *net.IPNet, ip *net.IP) int32 { - first, _ := networkdriver.NetworkRange(network) - return ipToInt(ip) - ipToInt(&first) -} +func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) { + pos := ipToInt(ip) -func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { - pos := getPosition(network, ip) + // Verify that the IP address has not been already allocated. if _, ok := allocated.p[pos]; ok { return nil, ErrIPAlreadyAllocated } + + // Verify that the IP address is within our network range. + if pos < allocated.begin || pos > allocated.end { + return nil, ErrIPOutOfRange + } + + // Register the IP. allocated.p[pos] = struct{}{} allocated.last = pos + return ip, nil } // return an available ip if one is currently available. If not, // return the next available ip for the nextwork -func (allocated *allocatedMap) getNextIP(network *net.IPNet) (*net.IP, error) { - var ( - ownIP = ipToInt(&network.IP) - first, _ = networkdriver.NetworkRange(network) - base = ipToInt(&first) - size = int(networkdriver.NetworkSize(network.Mask)) - max = int32(size - 2) // size -1 for the broadcast network, -1 for the gateway network - pos = allocated.last - ) - - var ( - firstNetIP = network.IP.To4().Mask(network.Mask) - firstAsInt = ipToInt(&firstNetIP) + 1 - ) - - for i := int32(0); i < max; i++ { - pos = pos%max + 1 - next := int32(base + pos) - - if next == ownIP || next == firstAsInt { - continue +func (allocated *allocatedMap) getNextIP() (net.IP, error) { + for pos := allocated.last + 1; pos != allocated.last; pos++ { + if pos > allocated.end { + pos = allocated.begin } if _, ok := allocated.p[pos]; ok { continue } allocated.p[pos] = struct{}{} allocated.last = pos - return intToIP(next), nil + return intToIP(pos), nil } return nil, ErrNoAvailableIPs } // Converts a 4 bytes IP into a 32 bit integer -func ipToInt(ip *net.IP) int32 { - return int32(binary.BigEndian.Uint32(ip.To4())) +func ipToInt(ip net.IP) uint32 { + return binary.BigEndian.Uint32(ip.To4()) } // Converts 32 bit integer into a 4 bytes IP address -func intToIP(n int32) *net.IP { +func intToIP(n uint32) net.IP { b := make([]byte, 4) - binary.BigEndian.PutUint32(b, uint32(n)) + binary.BigEndian.PutUint32(b, n) ip := net.IP(b) - return &ip + return ip } diff --git a/daemon/networkdriver/ipallocator/allocator_test.go b/daemon/networkdriver/ipallocator/allocator_test.go index 6897a0a44b..056c13b647 100644 --- a/daemon/networkdriver/ipallocator/allocator_test.go +++ b/daemon/networkdriver/ipallocator/allocator_test.go @@ -17,7 +17,7 @@ func TestRequestNewIps(t *testing.T) { Mask: []byte{255, 255, 255, 0}, } - var ip *net.IP + var ip net.IP var err error for i := 2; i < 10; i++ { ip, err = RequestIP(network, nil) @@ -97,28 +97,39 @@ func TestGetReleasedIp(t *testing.T) { } } -func TestRequesetSpecificIp(t *testing.T) { +func TestRequestSpecificIp(t *testing.T) { defer reset() network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, + Mask: []byte{255, 255, 255, 224}, } - ip := net.ParseIP("192.168.1.5") + ip := net.ParseIP("192.168.0.5") - if _, err := RequestIP(network, &ip); err != nil { + // Request a "good" IP. + if _, err := RequestIP(network, ip); err != nil { t.Fatal(err) } + + // Request the same IP again. + if _, err := RequestIP(network, ip); err != ErrIPAlreadyAllocated { + t.Fatalf("Got the same IP twice: %#v", err) + } + + // Request an out of range IP. + if _, err := RequestIP(network, net.ParseIP("192.168.0.42")); err != ErrIPOutOfRange { + t.Fatalf("Got an out of range IP: %#v", err) + } } func TestConversion(t *testing.T) { ip := net.ParseIP("127.0.0.1") - i := ipToInt(&ip) + i := ipToInt(ip) if i == 0 { t.Fatal("converted to zero") } conv := intToIP(i) - if !ip.Equal(*conv) { + if !ip.Equal(conv) { t.Error(conv.String()) } } @@ -146,7 +157,7 @@ func TestIPAllocator(t *testing.T) { t.Fatal(err) } - assertIPEquals(t, &expectedIPs[i], ip) + assertIPEquals(t, expectedIPs[i], ip) } // Before loop begin // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) @@ -179,19 +190,19 @@ func TestIPAllocator(t *testing.T) { } // Release some IPs in non-sequential order - if err := ReleaseIP(network, &expectedIPs[3]); err != nil { + if err := ReleaseIP(network, expectedIPs[3]); err != nil { t.Fatal(err) } // 2(u) - 3(u) - 4(u) - 5(f) - 6(u) // ↑ - if err := ReleaseIP(network, &expectedIPs[2]); err != nil { + if err := ReleaseIP(network, expectedIPs[2]); err != nil { t.Fatal(err) } // 2(u) - 3(u) - 4(f) - 5(f) - 6(u) // ↑ - if err := ReleaseIP(network, &expectedIPs[4]); err != nil { + if err := ReleaseIP(network, expectedIPs[4]); err != nil { t.Fatal(err) } // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) @@ -199,7 +210,7 @@ func TestIPAllocator(t *testing.T) { // Make sure that IPs are reused in sequential order, starting // with the first released IP - newIPs := make([]*net.IP, 3) + newIPs := make([]net.IP, 3) for i := 0; i < 3; i++ { ip, err := RequestIP(network, nil) if err != nil { @@ -208,9 +219,9 @@ func TestIPAllocator(t *testing.T) { newIPs[i] = ip } - assertIPEquals(t, &expectedIPs[2], newIPs[0]) - assertIPEquals(t, &expectedIPs[3], newIPs[1]) - assertIPEquals(t, &expectedIPs[4], newIPs[2]) + assertIPEquals(t, expectedIPs[2], newIPs[0]) + assertIPEquals(t, expectedIPs[3], newIPs[1]) + assertIPEquals(t, expectedIPs[4], newIPs[2]) _, err = RequestIP(network, nil) if err == nil { @@ -226,7 +237,7 @@ func TestAllocateFirstIP(t *testing.T) { } firstIP := network.IP.To4().Mask(network.Mask) - first := ipToInt(&firstIP) + 1 + first := ipToInt(firstIP) + 1 ip, err := RequestIP(network, nil) if err != nil { @@ -247,7 +258,7 @@ func TestAllocateAllIps(t *testing.T) { } var ( - current, first *net.IP + current, first net.IP err error isFirst = true ) @@ -313,14 +324,94 @@ func TestAllocateDifferentSubnets(t *testing.T) { if err != nil { t.Fatal(err) } - assertIPEquals(t, &expectedIPs[0], ip11) - assertIPEquals(t, &expectedIPs[1], ip12) - assertIPEquals(t, &expectedIPs[2], ip21) - assertIPEquals(t, &expectedIPs[3], ip22) + assertIPEquals(t, expectedIPs[0], ip11) + assertIPEquals(t, expectedIPs[1], ip12) + assertIPEquals(t, expectedIPs[2], ip21) + assertIPEquals(t, expectedIPs[3], ip22) +} +func TestRegisterBadTwice(t *testing.T) { + defer reset() + network := &net.IPNet{ + IP: []byte{192, 168, 1, 1}, + Mask: []byte{255, 255, 255, 0}, + } + subnet := &net.IPNet{ + IP: []byte{192, 168, 1, 8}, + Mask: []byte{255, 255, 255, 248}, + } + + if err := RegisterSubnet(network, subnet); err != nil { + t.Fatal(err) + } + subnet = &net.IPNet{ + IP: []byte{192, 168, 1, 16}, + Mask: []byte{255, 255, 255, 248}, + } + if err := RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered { + t.Fatalf("Expecteded ErrNetworkAlreadyRegistered error, got %v", err) + } } -func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) { - if !ip1.Equal(*ip2) { +func TestRegisterBadRange(t *testing.T) { + defer reset() + network := &net.IPNet{ + IP: []byte{192, 168, 1, 1}, + Mask: []byte{255, 255, 255, 0}, + } + subnet := &net.IPNet{ + IP: []byte{192, 168, 1, 1}, + Mask: []byte{255, 255, 0, 0}, + } + if err := RegisterSubnet(network, subnet); err != ErrBadSubnet { + t.Fatalf("Expected ErrBadSubnet error, got %v", err) + } +} + +func TestAllocateFromRange(t *testing.T) { + defer reset() + network := &net.IPNet{ + IP: []byte{192, 168, 0, 1}, + Mask: []byte{255, 255, 255, 0}, + } + // 192.168.1.9 - 192.168.1.14 + subnet := &net.IPNet{ + IP: []byte{192, 168, 0, 8}, + Mask: []byte{255, 255, 255, 248}, + } + if err := RegisterSubnet(network, subnet); err != nil { + t.Fatal(err) + } + expectedIPs := []net.IP{ + 0: net.IPv4(192, 168, 0, 9), + 1: net.IPv4(192, 168, 0, 10), + 2: net.IPv4(192, 168, 0, 11), + 3: net.IPv4(192, 168, 0, 12), + 4: net.IPv4(192, 168, 0, 13), + 5: net.IPv4(192, 168, 0, 14), + } + for _, ip := range expectedIPs { + rip, err := RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + assertIPEquals(t, ip, rip) + } + + if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs { + t.Fatalf("Expected ErrNoAvailableIPs error, got %v", err) + } + for _, ip := range expectedIPs { + ReleaseIP(network, ip) + rip, err := RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + assertIPEquals(t, ip, rip) + } +} + +func assertIPEquals(t *testing.T, ip1, ip2 net.IP) { + if !ip1.Equal(ip2) { t.Fatalf("Expected IP %s, got %s", ip1, ip2) } } diff --git a/daemon/networkdriver/portmapper/mapper.go b/daemon/networkdriver/portmapper/mapper.go index a81596d458..24ca0d892f 100644 --- a/daemon/networkdriver/portmapper/mapper.go +++ b/daemon/networkdriver/portmapper/mapper.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/daemon/networkdriver/portallocator" "github.com/docker/docker/pkg/iptables" + "github.com/docker/docker/pkg/log" ) type mapping struct { @@ -96,16 +97,25 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er return nil, err } - m.userlandProxy = proxy - currentMappings[key] = m - - if err := proxy.Start(); err != nil { + cleanup := func() error { // need to undo the iptables rules before we return + proxy.Stop() forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) + if err := portallocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { + return err + } - return nil, err + return nil } + if err := proxy.Start(); err != nil { + if err := cleanup(); err != nil { + return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) + } + return nil, err + } + m.userlandProxy = proxy + currentMappings[key] = m return m.host, nil } @@ -126,20 +136,15 @@ func Unmap(host net.Addr) error { containerIP, containerPort := getIPAndPort(data.container) hostIP, hostPort := getIPAndPort(data.host) if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { - return err + log.Errorf("Error on iptables delete: %s", err) } switch a := host.(type) { case *net.TCPAddr: - if err := portallocator.ReleasePort(a.IP, "tcp", a.Port); err != nil { - return err - } + return portallocator.ReleasePort(a.IP, "tcp", a.Port) case *net.UDPAddr: - if err := portallocator.ReleasePort(a.IP, "udp", a.Port); err != nil { - return err - } + return portallocator.ReleasePort(a.IP, "udp", a.Port) } - return nil } diff --git a/daemon/networkdriver/portmapper/proxy.go b/daemon/networkdriver/portmapper/proxy.go index b24723727b..341f0605e5 100644 --- a/daemon/networkdriver/portmapper/proxy.go +++ b/daemon/networkdriver/portmapper/proxy.go @@ -2,6 +2,8 @@ package portmapper import ( "flag" + "fmt" + "io/ioutil" "log" "net" "os" @@ -9,6 +11,7 @@ import ( "os/signal" "strconv" "syscall" + "time" "github.com/docker/docker/pkg/proxy" "github.com/docker/docker/reexec" @@ -33,14 +36,18 @@ type proxyCommand struct { // execProxy is the reexec function that is registered to start the userland proxies func execProxy() { + f := os.NewFile(3, "signal-parent") host, container := parseHostContainerAddrs() p, err := proxy.NewProxy(host, container) if err != nil { - log.Fatal(err) + fmt.Fprintf(f, "1\n%s", err) + f.Close() + os.Exit(1) } - go handleStopSignals(p) + fmt.Fprint(f, "0\n") + f.Close() // Run will block until the proxy stops p.Run() @@ -96,10 +103,8 @@ func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net. return &proxyCommand{ cmd: &exec.Cmd{ - Path: reexec.Self(), - Args: args, - Stdout: os.Stdout, - Stderr: os.Stderr, + Path: reexec.Self(), + Args: args, SysProcAttr: &syscall.SysProcAttr{ Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies }, @@ -108,12 +113,44 @@ func NewProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net. } func (p *proxyCommand) Start() error { - return p.cmd.Start() + r, w, err := os.Pipe() + if err != nil { + return fmt.Errorf("proxy unable to open os.Pipe %s", err) + } + defer r.Close() + p.cmd.ExtraFiles = []*os.File{w} + if err := p.cmd.Start(); err != nil { + return err + } + w.Close() + + errchan := make(chan error, 1) + go func() { + buf := make([]byte, 2) + r.Read(buf) + + if string(buf) != "0\n" { + errStr, _ := ioutil.ReadAll(r) + errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr) + return + } + errchan <- nil + }() + + select { + case err := <-errchan: + return err + case <-time.After(1 * time.Second): + return fmt.Errorf("Timed out proxy starting the userland proxy") + } } func (p *proxyCommand) Stop() error { - err := p.cmd.Process.Signal(os.Interrupt) - p.cmd.Wait() - - return err + if p.cmd.Process != nil { + if err := p.cmd.Process.Signal(os.Interrupt); err != nil { + return err + } + return p.cmd.Wait() + } + return nil } diff --git a/daemon/resize.go b/daemon/resize.go index dd196ff6c4..68c070370a 100644 --- a/daemon/resize.go +++ b/daemon/resize.go @@ -19,6 +19,7 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status { if err != nil { return job.Error(err) } + if container := daemon.Get(name); container != nil { if err := container.Resize(height, width); err != nil { return job.Error(err) @@ -27,3 +28,26 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status { } return job.Errorf("No such container: %s", name) } + +func (daemon *Daemon) ContainerExecResize(job *engine.Job) engine.Status { + if len(job.Args) != 3 { + return job.Errorf("Not enough arguments. Usage: %s EXEC HEIGHT WIDTH\n", job.Name) + } + name := job.Args[0] + height, err := strconv.Atoi(job.Args[1]) + if err != nil { + return job.Error(err) + } + width, err := strconv.Atoi(job.Args[2]) + if err != nil { + return job.Error(err) + } + execConfig, err := daemon.getExecConfig(name) + if err != nil { + return job.Error(err) + } + if err := execConfig.Resize(height, width); err != nil { + return job.Error(err) + } + return engine.StatusOK +} diff --git a/daemon/start.go b/daemon/start.go index 30e015496f..f2c375ddc9 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -22,11 +22,13 @@ func (daemon *Daemon) ContainerStart(job *engine.Job) engine.Status { return job.Errorf("No such container: %s", name) } - if container.State.IsRunning() { + if container.IsRunning() { return job.Errorf("Container already started") } // If no environment was set, then no hostconfig was passed. + // This is kept for backward compatibility - hostconfig should be passed when + // creating a container, not during start. if len(job.Environ()) > 0 { hostConfig := runconfig.ContainerHostConfigFromJob(job) if err := daemon.setHostConfig(container, hostConfig); err != nil { @@ -34,6 +36,7 @@ func (daemon *Daemon) ContainerStart(job *engine.Job) engine.Status { } } if err := container.Start(); err != nil { + container.LogEvent("die") return job.Errorf("Cannot start container %s: %s", name, err) } diff --git a/daemon/state.go b/daemon/state.go index 44742b78c9..b7dc149959 100644 --- a/daemon/state.go +++ b/daemon/state.go @@ -1,7 +1,6 @@ package daemon import ( - "encoding/json" "fmt" "sync" "time" @@ -10,7 +9,7 @@ import ( ) type State struct { - sync.RWMutex + sync.Mutex Running bool Paused bool Restarting bool @@ -29,9 +28,6 @@ func NewState() *State { // String returns a human-readable description of the state func (s *State) String() string { - s.RLock() - defer s.RUnlock() - if s.Running { if s.Paused { return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) @@ -50,14 +46,18 @@ func (s *State) String() string { return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) } -type jState State - -// MarshalJSON for state is needed to avoid race conditions on inspect -func (s *State) MarshalJSON() ([]byte, error) { - s.RLock() - b, err := json.Marshal(jState(*s)) - s.RUnlock() - return b, err +// StateString returns a single string to describe state +func (s *State) StateString() string { + if s.Running { + if s.Paused { + return "paused" + } + if s.Restarting { + return "restarting" + } + return "running" + } + return "exited" } func wait(waitChan <-chan struct{}, timeout time.Duration) error { @@ -74,17 +74,17 @@ func wait(waitChan <-chan struct{}, timeout time.Duration) error { } // WaitRunning waits until state is running. If state already running it returns -// immediatly. If you want wait forever you must supply negative timeout. +// immediately. If you want wait forever you must supply negative timeout. // Returns pid, that was passed to SetRunning func (s *State) WaitRunning(timeout time.Duration) (int, error) { - s.RLock() - if s.IsRunning() { + s.Lock() + if s.Running { pid := s.Pid - s.RUnlock() + s.Unlock() return pid, nil } waitChan := s.waitChan - s.RUnlock() + s.Unlock() if err := wait(waitChan, timeout); err != nil { return -1, err } @@ -92,17 +92,17 @@ func (s *State) WaitRunning(timeout time.Duration) (int, error) { } // WaitStop waits until state is stopped. If state already stopped it returns -// immediatly. If you want wait forever you must supply negative timeout. +// immediately. If you want wait forever you must supply negative timeout. // Returns exit code, that was passed to SetStopped func (s *State) WaitStop(timeout time.Duration) (int, error) { - s.RLock() + s.Lock() if !s.Running { exitCode := s.ExitCode - s.RUnlock() + s.Unlock() return exitCode, nil } waitChan := s.waitChan - s.RUnlock() + s.Unlock() if err := wait(waitChan, timeout); err != nil { return -1, err } @@ -110,28 +110,33 @@ func (s *State) WaitStop(timeout time.Duration) (int, error) { } func (s *State) IsRunning() bool { - s.RLock() + s.Lock() res := s.Running - s.RUnlock() + s.Unlock() return res } func (s *State) GetPid() int { - s.RLock() + s.Lock() res := s.Pid - s.RUnlock() + s.Unlock() return res } func (s *State) GetExitCode() int { - s.RLock() + s.Lock() res := s.ExitCode - s.RUnlock() + s.Unlock() return res } func (s *State) SetRunning(pid int) { s.Lock() + s.setRunning(pid) + s.Unlock() +} + +func (s *State) setRunning(pid int) { s.Running = true s.Paused = false s.Restarting = false @@ -140,11 +145,15 @@ func (s *State) SetRunning(pid int) { s.StartedAt = time.Now().UTC() close(s.waitChan) // fire waiters for start s.waitChan = make(chan struct{}) - s.Unlock() } func (s *State) SetStopped(exitCode int) { s.Lock() + s.setStopped(exitCode) + s.Unlock() +} + +func (s *State) setStopped(exitCode int) { s.Running = false s.Restarting = false s.Pid = 0 @@ -152,7 +161,6 @@ func (s *State) SetStopped(exitCode int) { s.ExitCode = exitCode close(s.waitChan) // fire waiters for stop s.waitChan = make(chan struct{}) - s.Unlock() } // SetRestarting is when docker hanldes the auto restart of containers when they are @@ -172,9 +180,9 @@ func (s *State) SetRestarting(exitCode int) { } func (s *State) IsRestarting() bool { - s.RLock() + s.Lock() res := s.Restarting - s.RUnlock() + s.Unlock() return res } @@ -191,8 +199,8 @@ func (s *State) SetUnpaused() { } func (s *State) IsPaused() bool { - s.RLock() + s.Lock() res := s.Paused - s.RUnlock() + s.Unlock() return res } diff --git a/daemon/stop.go b/daemon/stop.go index f1851291fb..1a098a1ad3 100644 --- a/daemon/stop.go +++ b/daemon/stop.go @@ -16,7 +16,7 @@ func (daemon *Daemon) ContainerStop(job *engine.Job) engine.Status { t = job.GetenvInt("t") } if container := daemon.Get(name); container != nil { - if !container.State.IsRunning() { + if !container.IsRunning() { return job.Errorf("Container already stopped") } if err := container.Stop(int(t)); err != nil { diff --git a/daemon/top.go b/daemon/top.go index ceaeea157e..4d916ee5dc 100644 --- a/daemon/top.go +++ b/daemon/top.go @@ -22,14 +22,14 @@ func (daemon *Daemon) ContainerTop(job *engine.Job) engine.Status { } if container := daemon.Get(name); container != nil { - if !container.State.IsRunning() { + if !container.IsRunning() { return job.Errorf("Container %s is not running", name) } pids, err := daemon.ExecutionDriver().GetPidsForContainer(container.ID) if err != nil { return job.Error(err) } - output, err := exec.Command("ps", psArgs).Output() + output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output() if err != nil { return job.Errorf("Error running ps: %s", err) } diff --git a/daemon/utils.go b/daemon/utils.go index 053319c5ea..9c43236e0b 100644 --- a/daemon/utils.go +++ b/daemon/utils.go @@ -32,20 +32,22 @@ func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostCon return nil } -func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig, driverConfig map[string][]string) { +func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig) []string { if hostConfig == nil { - return + return nil } + out := []string{} + // merge in the lxc conf options into the generic config map if lxcConf := hostConfig.LxcConf; lxcConf != nil { - lxc := driverConfig["lxc"] for _, pair := range lxcConf { // because lxc conf gets the driver name lxc.XXXX we need to trim it off // and let the lxc driver add it back later if needed parts := strings.SplitN(pair.Key, ".", 2) - lxc = append(lxc, fmt.Sprintf("%s=%s", parts[1], pair.Value)) + out = append(out, fmt.Sprintf("%s=%s", parts[1], pair.Value)) } - driverConfig["lxc"] = lxc } + + return out } diff --git a/daemon/utils_linux.go b/daemon/utils_linux.go index bff2a787b1..fb35152f0d 100644 --- a/daemon/utils_linux.go +++ b/daemon/utils_linux.go @@ -11,3 +11,7 @@ func selinuxSetDisabled() { func selinuxFreeLxcContexts(label string) { selinux.FreeLxcContexts(label) } + +func selinuxEnabled() bool { + return selinux.SelinuxEnabled() +} diff --git a/daemon/utils_nolinux.go b/daemon/utils_nolinux.go index 399376dbd4..25a56ad157 100644 --- a/daemon/utils_nolinux.go +++ b/daemon/utils_nolinux.go @@ -7,3 +7,7 @@ func selinuxSetDisabled() { func selinuxFreeLxcContexts(label string) { } + +func selinuxEnabled() bool { + return false +} diff --git a/daemon/utils_test.go b/daemon/utils_test.go index 1f3175b994..7748b86022 100644 --- a/daemon/utils_test.go +++ b/daemon/utils_test.go @@ -8,22 +8,47 @@ import ( ) func TestMergeLxcConfig(t *testing.T) { - var ( - hostConfig = &runconfig.HostConfig{ - LxcConf: []utils.KeyValuePair{ - {Key: "lxc.cgroups.cpuset", Value: "1,2"}, - }, - } - driverConfig = make(map[string][]string) - ) - - mergeLxcConfIntoOptions(hostConfig, driverConfig) - if l := len(driverConfig["lxc"]); l > 1 { - t.Fatalf("expected lxc options len of 1 got %d", l) + hostConfig := &runconfig.HostConfig{ + LxcConf: []utils.KeyValuePair{ + {Key: "lxc.cgroups.cpuset", Value: "1,2"}, + }, } - cpuset := driverConfig["lxc"][0] + out := mergeLxcConfIntoOptions(hostConfig) + + cpuset := out[0] if expected := "cgroups.cpuset=1,2"; cpuset != expected { t.Fatalf("expected %s got %s", expected, cpuset) } } + +func TestRemoveLocalDns(t *testing.T) { + ns0 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\n" + + if result := utils.RemoveLocalDns([]byte(ns0)); result != nil { + if ns0 != string(result) { + t.Fatalf("Failed No Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) + } + } + + ns1 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n" + if result := utils.RemoveLocalDns([]byte(ns1)); result != nil { + if ns0 != string(result) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) + } + } + + ns1 = "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n" + if result := utils.RemoveLocalDns([]byte(ns1)); result != nil { + if ns0 != string(result) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) + } + } + + ns1 = "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n" + if result := utils.RemoveLocalDns([]byte(ns1)); result != nil { + if ns0 != string(result) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result)) + } + } +} diff --git a/daemon/volumes.go b/daemon/volumes.go index b60118c953..c7a8d7bfcb 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -5,307 +5,284 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strings" "syscall" - "github.com/docker/docker/archive" "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/log" "github.com/docker/docker/pkg/symlink" + "github.com/docker/docker/volumes" ) -type Volume struct { - HostPath string - VolPath string - Mode string - isBindMount bool +type Mount struct { + MountToPath string + container *Container + volume *volumes.Volume + Writable bool + copyData bool } -func (v *Volume) isRw() bool { - return v.Mode == "" || strings.ToLower(v.Mode) == "rw" -} - -func (v *Volume) isDir() (bool, error) { - stat, err := os.Stat(v.HostPath) - if err != nil { - return false, err - } - - return stat.IsDir(), nil -} - -func prepareVolumesForContainer(container *Container) error { +func (container *Container) prepareVolumes() error { if container.Volumes == nil || len(container.Volumes) == 0 { container.Volumes = make(map[string]string) container.VolumesRW = make(map[string]bool) - if err := applyVolumesFrom(container); err != nil { + if err := container.applyVolumesFrom(); err != nil { return err } } - if err := createVolumes(container); err != nil { + return container.createVolumes() +} + +// sortedVolumeMounts returns the list of container volume mount points sorted in lexicographic order +func (container *Container) sortedVolumeMounts() []string { + var mountPaths []string + for path := range container.Volumes { + mountPaths = append(mountPaths, path) + } + + sort.Strings(mountPaths) + return mountPaths +} + +func (container *Container) createVolumes() error { + mounts, err := container.parseVolumeMountConfig() + if err != nil { return err } + + for _, mnt := range mounts { + if err := mnt.initialize(); err != nil { + return err + } + } + + return nil +} + +func (m *Mount) initialize() error { + // No need to initialize anything since it's already been initialized + if _, exists := m.container.Volumes[m.MountToPath]; exists { + return nil + } + + // This is the full path to container fs + mntToPath + containerMntPath, err := symlink.FollowSymlinkInScope(filepath.Join(m.container.basefs, m.MountToPath), m.container.basefs) + if err != nil { + return err + } + m.container.VolumesRW[m.MountToPath] = m.Writable + m.container.Volumes[m.MountToPath] = m.volume.Path + m.volume.AddContainer(m.container.ID) + if m.Writable && m.copyData { + // Copy whatever is in the container at the mntToPath to the volume + copyExistingContents(containerMntPath, m.volume.Path) + } + + return nil +} + +func (container *Container) VolumePaths() map[string]struct{} { + var paths = make(map[string]struct{}) + for _, path := range container.Volumes { + paths[path] = struct{}{} + } + return paths +} + +func (container *Container) registerVolumes() { + for _, mnt := range container.VolumeMounts() { + mnt.volume.AddContainer(container.ID) + } +} + +func (container *Container) derefVolumes() { + for path := range container.VolumePaths() { + vol := container.daemon.volumes.Get(path) + if vol == nil { + log.Debugf("Volume %s was not found and could not be dereferenced", path) + continue + } + vol.RemoveContainer(container.ID) + } +} + +func (container *Container) parseVolumeMountConfig() (map[string]*Mount, error) { + var mounts = make(map[string]*Mount) + // Get all the bind mounts + for _, spec := range container.hostConfig.Binds { + path, mountToPath, writable, err := parseBindMountSpec(spec) + if err != nil { + return nil, err + } + // Check if a volume already exists for this and use it + vol, err := container.daemon.volumes.FindOrCreateVolume(path, writable) + if err != nil { + return nil, err + } + mounts[mountToPath] = &Mount{ + container: container, + volume: vol, + MountToPath: mountToPath, + Writable: writable, + } + } + + // Get the rest of the volumes + for path := range container.Config.Volumes { + // Check if this is already added as a bind-mount + if _, exists := mounts[path]; exists { + continue + } + + // Check if this has already been created + if _, exists := container.Volumes[path]; exists { + continue + } + + vol, err := container.daemon.volumes.FindOrCreateVolume("", true) + if err != nil { + return nil, err + } + mounts[path] = &Mount{ + container: container, + MountToPath: path, + volume: vol, + Writable: true, + copyData: true, + } + } + + return mounts, nil +} + +func parseBindMountSpec(spec string) (string, string, bool, error) { + var ( + path, mountToPath string + writable bool + arr = strings.Split(spec, ":") + ) + + switch len(arr) { + case 2: + path = arr[0] + mountToPath = arr[1] + writable = true + case 3: + path = arr[0] + mountToPath = arr[1] + writable = validMountMode(arr[2]) && arr[2] == "rw" + default: + return "", "", false, fmt.Errorf("Invalid volume specification: %s", spec) + } + + if !filepath.IsAbs(path) { + return "", "", false, fmt.Errorf("cannot bind mount volume: %s volume paths must be absolute.", path) + } + + return path, mountToPath, writable, nil +} + +func (container *Container) applyVolumesFrom() error { + volumesFrom := container.hostConfig.VolumesFrom + + for _, spec := range volumesFrom { + mounts, err := parseVolumesFromSpec(container.daemon, spec) + if err != nil { + return err + } + + for _, mnt := range mounts { + mnt.container = container + if err = mnt.initialize(); err != nil { + return err + } + } + } return nil } -func setupMountsForContainer(container *Container) error { +func validMountMode(mode string) bool { + validModes := map[string]bool{ + "rw": true, + "ro": true, + } + + return validModes[mode] +} + +func (container *Container) setupMounts() error { mounts := []execdriver.Mount{ - {container.ResolvConfPath, "/etc/resolv.conf", true, true}, + {Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true}, } if container.HostnamePath != "" { - mounts = append(mounts, execdriver.Mount{container.HostnamePath, "/etc/hostname", true, true}) + mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true}) } if container.HostsPath != "" { - mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", true, true}) + mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true}) } // Mount user specified volumes // Note, these are not private because you may want propagation of (un)mounts from host // volumes. For instance if you use -v /usr:/usr and the host later mounts /usr/share you // want this new mount in the container - for r, v := range container.Volumes { - mounts = append(mounts, execdriver.Mount{v, r, container.VolumesRW[r], false}) + // These mounts must be ordered based on the length of the path that it is being mounted to (lexicographic) + for _, path := range container.sortedVolumeMounts() { + mounts = append(mounts, execdriver.Mount{ + Source: container.Volumes[path], + Destination: path, + Writable: container.VolumesRW[path], + }) } container.command.Mounts = mounts - return nil } -func applyVolumesFrom(container *Container) error { - volumesFrom := container.hostConfig.VolumesFrom - if len(volumesFrom) > 0 { - for _, containerSpec := range volumesFrom { - var ( - mountRW = true - specParts = strings.SplitN(containerSpec, ":", 2) - ) - - switch len(specParts) { - case 0: - return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec) - case 2: - switch specParts[1] { - case "ro": - mountRW = false - case "rw": // mountRW is already true - default: - return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec) - } - } - - c := container.daemon.Get(specParts[0]) - if c == nil { - return fmt.Errorf("Container %s not found. Impossible to mount its volumes", specParts[0]) - } - - if err := c.Mount(); err != nil { - return fmt.Errorf("Container %s failed to mount. Impossible to mount its volumes", specParts[0]) - } - defer c.Unmount() - - for volPath, id := range c.Volumes { - if _, exists := container.Volumes[volPath]; exists { - continue - } - - pth, err := c.getResourcePath(volPath) - if err != nil { - return err - } - - stat, err := os.Stat(pth) - if err != nil { - return err - } - - if err := createIfNotExists(pth, stat.IsDir()); err != nil { - return err - } - - container.Volumes[volPath] = id - if isRW, exists := c.VolumesRW[volPath]; exists { - container.VolumesRW[volPath] = isRW && mountRW - } - } - - } - } - return nil -} - -func parseBindVolumeSpec(spec string) (Volume, error) { - var ( - arr = strings.Split(spec, ":") - vol Volume - ) - - vol.isBindMount = true - switch len(arr) { - case 1: - vol.VolPath = spec - vol.Mode = "rw" - case 2: - vol.HostPath = arr[0] - vol.VolPath = arr[1] - vol.Mode = "rw" - case 3: - vol.HostPath = arr[0] - vol.VolPath = arr[1] - vol.Mode = arr[2] - default: - return vol, fmt.Errorf("Invalid volume specification: %s", spec) +func parseVolumesFromSpec(daemon *Daemon, spec string) (map[string]*Mount, error) { + specParts := strings.SplitN(spec, ":", 2) + if len(specParts) == 0 { + return nil, fmt.Errorf("Malformed volumes-from specification: %s", spec) } - if !filepath.IsAbs(vol.HostPath) { - return vol, fmt.Errorf("cannot bind mount volume: %s volume paths must be absolute.", vol.HostPath) + c := daemon.Get(specParts[0]) + if c == nil { + return nil, fmt.Errorf("Container %s not found. Impossible to mount its volumes", specParts[0]) } - return vol, nil -} + mounts := c.VolumeMounts() -func getBindMap(container *Container) (map[string]Volume, error) { - var ( - // Create the requested bind mounts - volumes = map[string]Volume{} - // Define illegal container destinations - illegalDsts = []string{"/", "."} - ) - - for _, bind := range container.hostConfig.Binds { - vol, err := parseBindVolumeSpec(bind) - if err != nil { - return volumes, err - } - // Bail if trying to mount to an illegal destination - for _, illegal := range illegalDsts { - if vol.VolPath == illegal { - return nil, fmt.Errorf("Illegal bind destination: %s", vol.VolPath) - } + if len(specParts) == 2 { + mode := specParts[1] + if !validMountMode(mode) { + return nil, fmt.Errorf("Invalid mode for volumes-from: %s", mode) } - volumes[filepath.Clean(vol.VolPath)] = vol - } - return volumes, nil -} - -func createVolumes(container *Container) error { - // Get all the bindmounts - volumes, err := getBindMap(container) - if err != nil { - return err - } - - // Get all the rest of the volumes - for volPath := range container.Config.Volumes { - // Make sure the the volume isn't already specified as a bindmount - if _, exists := volumes[volPath]; !exists { - volumes[volPath] = Volume{ - VolPath: volPath, - Mode: "rw", - isBindMount: false, - } + // Set the mode for the inheritted volume + for _, mnt := range mounts { + // Ensure that if the inherited volume is not writable, that we don't make + // it writable here + mnt.Writable = mnt.Writable && (mode == "rw") } } - for _, vol := range volumes { - if err = vol.initialize(container); err != nil { - return err - } - } - return nil - + return mounts, nil } -func createVolumeHostPath(container *Container) (string, error) { - volumesDriver := container.daemon.volumes.Driver() +func (container *Container) VolumeMounts() map[string]*Mount { + mounts := make(map[string]*Mount) - // Do not pass a container as the parameter for the volume creation. - // The graph driver using the container's information ( Image ) to - // create the parent. - c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil) - if err != nil { - return "", err - } - hostPath, err := volumesDriver.Get(c.ID, "") - if err != nil { - return hostPath, fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err) - } - - return hostPath, nil -} - -func (v *Volume) initialize(container *Container) error { - var err error - v.VolPath = filepath.Clean(v.VolPath) - - // Do not initialize an existing volume - if _, exists := container.Volumes[v.VolPath]; exists { - return nil - } - - // If it's not a bindmount we need to create the dir on the host - if !v.isBindMount { - v.HostPath, err = createVolumeHostPath(container) - if err != nil { - return err + for mountToPath, path := range container.Volumes { + if v := container.daemon.volumes.Get(path); v != nil { + mounts[mountToPath] = &Mount{volume: v, container: container, MountToPath: mountToPath, Writable: container.VolumesRW[mountToPath]} } } - hostPath, err := filepath.EvalSymlinks(v.HostPath) - if err != nil { - return err - } - - // Create the mountpoint - // This is the path to the volume within the container FS - // This differs from `hostPath` in that `hostPath` refers to the place where - // the volume data is actually stored on the host - fullVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, v.VolPath), container.basefs) - if err != nil { - return err - } - - container.Volumes[v.VolPath] = hostPath - container.VolumesRW[v.VolPath] = v.isRw() - - volIsDir, err := v.isDir() - if err != nil { - return err - } - if err := createIfNotExists(fullVolPath, volIsDir); err != nil { - return err - } - - // Do not copy or change permissions if we are mounting from the host - if v.isRw() && !v.isBindMount { - return copyExistingContents(fullVolPath, hostPath) - } - return nil -} - -func createIfNotExists(destination string, isDir bool) error { - if _, err := os.Stat(destination); err == nil || !os.IsNotExist(err) { - return nil - } - - if isDir { - return os.MkdirAll(destination, 0755) - } - - if err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil { - return err - } - - f, err := os.OpenFile(destination, os.O_CREATE, 0755) - if err != nil { - return err - } - f.Close() - - return nil + return mounts } func copyExistingContents(source, destination string) error { diff --git a/daemon/wait.go b/daemon/wait.go index 7224b6231f..a1f657c353 100644 --- a/daemon/wait.go +++ b/daemon/wait.go @@ -12,9 +12,9 @@ func (daemon *Daemon) ContainerWait(job *engine.Job) engine.Status { } name := job.Args[0] if container := daemon.Get(name); container != nil { - status, _ := container.State.WaitStop(-1 * time.Second) + status, _ := container.WaitStop(-1 * time.Second) job.Printf("%d\n", status) return engine.StatusOK } - return job.Errorf("%s: no such container: %s", job.Name, name) + return job.Errorf("%s: No such container: %s", job.Name, name) } diff --git a/docker/daemon.go b/docker/daemon.go index dc9d56d1d9..eef17efdc4 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -5,6 +5,7 @@ package main import ( "log" + "github.com/docker/docker/builder" "github.com/docker/docker/builtins" "github.com/docker/docker/daemon" _ "github.com/docker/docker/daemon/execdriver/lxc" @@ -48,6 +49,10 @@ func mainDaemon() { if err := d.Install(eng); err != nil { log.Fatal(err) } + + b := &builder.BuilderJob{eng, d} + b.Install() + // after the daemon is done setting up we can tell the api to start // accepting connections if err := eng.Job("acceptconnections").Run(); err != nil { diff --git a/docker/docker.go b/docker/docker.go index f2b4ca90b1..37cd155bb7 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -18,9 +18,10 @@ import ( ) const ( - defaultCaFile = "ca.pem" - defaultKeyFile = "key.pem" - defaultCertFile = "cert.pem" + defaultTrustKeyFile = "key.json" + defaultCaFile = "ca.pem" + defaultKeyFile = "key.pem" + defaultCertFile = "cert.pem" ) func main() { @@ -44,7 +45,8 @@ func main() { // If we do not have a host, default to unix socket defaultHost = fmt.Sprintf("unix://%s", api.DEFAULTUNIXSOCKET) } - if _, err := api.ValidateHost(defaultHost); err != nil { + defaultHost, err := api.ValidateHost(defaultHost) + if err != nil { log.Fatal(err) } flHosts = append(flHosts, defaultHost) @@ -94,9 +96,9 @@ func main() { } if *flTls || *flTlsVerify { - cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig) + cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, nil, protoAddrParts[0], protoAddrParts[1], &tlsConfig) } else { - cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil) + cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, nil, protoAddrParts[0], protoAddrParts[1], nil) } if err := cli.Cmd(flag.Args()...); err != nil { diff --git a/docker/flags.go b/docker/flags.go index baae40eafc..61081ec996 100644 --- a/docker/flags.go +++ b/docker/flags.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "path/filepath" @@ -9,7 +10,8 @@ import ( ) var ( - dockerCertPath = os.Getenv("DOCKER_CERT_PATH") + dockerCertPath = os.Getenv("DOCKER_CERT_PATH") + dockerTlsVerify = os.Getenv("DOCKER_TLS_VERIFY") != "" ) func init() { @@ -25,18 +27,75 @@ var ( flSocketGroup = flag.String([]string{"G", "-group"}, "docker", "Group to assign the unix socket specified by -H when running in daemon mode\nuse '' (the empty string) to disable setting of a group") flEnableCors = flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"}, false, "Enable CORS headers in the remote API") flTls = flag.Bool([]string{"-tls"}, false, "Use TLS; implied by tls-verify flags") - flTlsVerify = flag.Bool([]string{"-tlsverify"}, false, "Use TLS and verify the remote (daemon: verify client, client: verify daemon)") + flTlsVerify = flag.Bool([]string{"-tlsverify"}, dockerTlsVerify, "Use TLS and verify the remote (daemon: verify client, client: verify daemon)") // these are initialized in init() below since their default values depend on dockerCertPath which isn't fully initialized until init() runs - flCa *string - flCert *string - flKey *string - flHosts []string + flTrustKey *string + flCa *string + flCert *string + flKey *string + flHosts []string ) func init() { + // placeholder for trust key flag + trustKeyDefault := filepath.Join(dockerCertPath, defaultTrustKeyFile) + flTrustKey = &trustKeyDefault + flCa = flag.String([]string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust only remotes providing a certificate signed by the CA given here") flCert = flag.String([]string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file") flKey = flag.String([]string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file") - opts.HostListVar(&flHosts, []string{"H", "-host"}, "The socket(s) to bind to in daemon mode\nspecified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.") + opts.HostListVar(&flHosts, []string{"H", "-host"}, "The socket(s) to bind to in daemon mode or connect to in client mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.") + + flag.Usage = func() { + fmt.Fprint(os.Stderr, "Usage: docker [OPTIONS] COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nOptions:\n") + + flag.PrintDefaults() + + help := "\nCommands:\n" + + for _, command := range [][]string{ + {"attach", "Attach to a running container"}, + {"build", "Build an image from a Dockerfile"}, + {"commit", "Create a new image from a container's changes"}, + {"cp", "Copy files/folders from a container's filesystem to the host path"}, + {"create", "Create a new container"}, + {"diff", "Inspect changes on a container's filesystem"}, + {"events", "Get real time events from the server"}, + {"exec", "Run a command in an existing container"}, + {"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"}, + {"load", "Load an image from a tar archive"}, + {"login", "Register or log in to a Docker registry server"}, + {"logout", "Log out from a Docker registry server"}, + {"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"}, + {"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"}, + {"restart", "Restart a running container"}, + {"rm", "Remove one or more containers"}, + {"rmi", "Remove one or more images"}, + {"run", "Run a command in a new container"}, + {"save", "Save an image to a tar archive"}, + {"search", "Search for an image on the Docker Hub"}, + {"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"}, + } { + help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1]) + } + help += "\nRun 'docker COMMAND --help' for more information on a command." + fmt.Fprintf(os.Stderr, "%s\n", help) + } } diff --git a/docs/Dockerfile b/docs/Dockerfile index a50b396624..3c58193b99 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -4,7 +4,7 @@ FROM debian:jessie MAINTAINER Sven Dowideit (@SvenDowideit) -RUN apt-get update && apt-get install -y make python-pip python-setuptools vim-tiny git gettext +RUN apt-get update && apt-get install -y make python-pip python-setuptools vim-tiny git gettext python-dev libssl-dev RUN pip install mkdocs @@ -14,7 +14,7 @@ RUN pip install mkdocs #RUN pip install MarkdownTools2 # this version works, the current versions fail in different ways -RUN pip install awscli==1.3.9 +RUN pip install awscli==1.4.4 pyopenssl==0.12 # make sure the git clone is not an old cache - we've published old versions a few times now ENV CACHE_BUST Jul2014 diff --git a/docs/MAINTAINERS b/docs/MAINTAINERS index 55489fd5c2..d07b531d72 100644 --- a/docs/MAINTAINERS +++ b/docs/MAINTAINERS @@ -1,4 +1,4 @@ +Fred Lifton (@fredlf) James Turnbull (@jamtur01) Sven Dowideit (@SvenDowideit) O.S. Tezer (@OSTezer) -Fred Lifton (@fredlf) diff --git a/docs/README.md b/docs/README.md index ba1feb50d4..27ed7eef11 100755 --- a/docs/README.md +++ b/docs/README.md @@ -9,28 +9,6 @@ GitHub](https://github.com/docker/docker) thanks to post-commit hooks. The `docs` branch maps to the "latest" documentation and the `master` (unreleased development) branch maps to the "master" documentation. -## Branches - -**There are two branches related to editing docs**: `master` and a `docs` -branch. You should always edit documentation on a local branch of the `master` -branch, and send a PR against `master`. - -That way your fixes will automatically get included in later releases, and docs -maintainers can easily cherry-pick your changes into the `docs` release branch. -In the rare case where your change is not forward-compatible, you may need to -base your changes on the `docs` branch. - -Also, now that we have a `docs` branch, we can keep the -[http://docs.docker.com](http://docs.docker.com) docs up to date with any bugs -found between Docker code releases. - -**Warning**: When *reading* the docs, the -[http://docs-stage.docker.com](http://docs-stage.docker.com) documentation may -include features not yet part of any official Docker release. The `beta-docs` -site should be used only for understanding bleeding-edge development and -`docs.docker.com` (which points to the `docs` branch`) should be used for the -latest official release. - ## Contributing - Follow the contribution guidelines ([see @@ -46,11 +24,21 @@ changes. In the root of the `docker` source directory: - make docs + $ make docs + .... (lots of output) .... + $ docker run --rm -it -e AWS_S3_BUCKET -p 8000:8000 "docker-docs:master" mkdocs serve + Running at: http://0.0.0.0:8000/ + Live reload enabled. + Hold ctrl+c to quit. If you have any issues you need to debug, you can use `make docs-shell` and then run `mkdocs serve` +## Adding a new document + +New document (`.md`) files are added to the documentation builds by adding them +to the menu definition in the `docs/mkdocs.yml` file. + ## Style guide The documentation is written with paragraphs wrapped at 80 column lines to make @@ -83,11 +71,33 @@ Markdown](http://www.mkdocs.org/user-guide/writing-your-docs/)). Just be careful not to create many commits. And you must still [sign your work!](../CONTRIBUTING.md#sign-your-work) +## Branches + +**There are two branches related to editing docs**: `master` and a `docs` +branch. You should always edit the documentation on a local branch of the `master` +branch, and send a PR against `master`. + +That way your edits will automatically get included in later releases, and docs +maintainers can easily cherry-pick your changes into the `docs` release branch. +In the rare case where your change is not forward-compatible, you may need to +base your changes on the `docs` branch. + +Also, now that we have a `docs` branch, we can keep the +[http://docs.docker.com](http://docs.docker.com) docs up to date with any bugs +found between Docker code releases. + +> **Warning**: When *reading* the docs, the +> [http://docs-stage.docker.com](http://docs-stage.docker.com) documentation may +> include features not yet part of any official Docker release. The `beta-docs` +> site should be used only for understanding bleeding-edge development and +> `docs.docker.com` (which points to the `docs` branch`) should be used for the +> latest official release. + ## Publishing Documentation -To publish a copy of the documentation you need a `docs/awsconfig` -file containing AWS settings to deploy to. The release script will -create an s3 if needed, and will then push the files to it. +To publish a copy of the documentation you need to have Docker up and running on your +machine. You'll also need a `docs/awsconfig` file containing AWS settings to deploy to. +The release script will create an s3 if needed, and will then push the files to it. [profile dowideit-docs] aws_access_key_id = IHOIUAHSIDH234rwf.... aws_secret_access_key = OIUYSADJHLKUHQWIUHE...... region = ap-southeast-2 @@ -97,3 +107,56 @@ to - which you call from the `docker` directory: make AWS_S3_BUCKET=dowideit-docs docs-release +This will publish _only_ to the `http://bucket-url/v1.2/` version of the +documentation. + +If you're publishing the current release's documentation, you need to +also update the root docs pages by running + + make AWS_S3_BUCKET=dowideit-docs BUILD_ROOT=yes docs-release + +> **Note:** if you are using Boot2Docker on OSX and the above command returns an error, +> `Post http:///var/run/docker.sock/build?rm=1&t=docker-docs%3Apost-1.2.0-docs_update-2: +> dial unix /var/run/docker.sock: no such file or directory', you need to set the Docker +> host. Run `$(boot2docker shellinit)` to see the correct variable to set. The command +> will return the full `export` command, so you can just cut and paste. + +## Cherry-picking documentation changes to update an existing release. + +Whenever the core team makes a release, they publish the documentation based +on the `release` branch (which is copied into the `docs` branch). The +documentation team can make updates in the meantime, by cherry-picking changes +from `master` into any of the docs branches. + +For example, to update the current release's docs: + + git fetch upstream + git checkout -b post-1.2.0-docs-update-1 upstream/docs + # Then go through the Merge commit linked to PR's (making sure they apply + to that release) + # see https://github.com/docker/docker/commits/master + git cherry-pick -x fe845c4 + # Repeat until you have cherry picked everything you will propose to be merged + git push upstream post-1.2.0-docs-update-1 + +Then make a pull request to merge into the `docs` branch, __NOT__ into master. + +Once the PR has the needed `LGTM`s, merge it, then publish to our beta server +to test: + + git fetch upstream + git checkout post-1.2.0-docs-update-1 + git reset --hard upstream/post-1.2.0-docs-update-1 + make AWS_S3_BUCKET=beta-docs.docker.io BUILD_ROOT=yes docs-release + +Then go to http://beta-docs.docker.io.s3-website-us-west-2.amazonaws.com/ +to view your results and make sure what you published is what you wanted. + +When you're happy with it, publish the docs to our live site: + + make AWS_S3_BUCKET=docs.docker.com BUILD_ROOT=yes docs-release + +Note that the new docs will not appear live on the site until the cache (a complex, +distributed CDN system) is flushed. This requires someone with S3 keys. Contact Docker +(Sven Dowideit or John Costa) for assistance. + diff --git a/docs/docs-update.py b/docs/docs-update.py index 2ff305c5ab..b605aeccb2 100755 --- a/docs/docs-update.py +++ b/docs/docs-update.py @@ -16,32 +16,35 @@ import os.path script, docker_cmd = argv -# date "+%B %Y" date_string = datetime.date.today().strftime('%B %Y') + def print_usage(outtext, docker_cmd, command): - help = "" try: - #print "RUN ", "".join((docker_cmd, " ", command, " --help")) - help = subprocess.check_output("".join((docker_cmd, " ", command, " --help")), stderr=subprocess.STDOUT, shell=True) + help_string = subprocess.check_output( + "".join((docker_cmd, " ", command, " --help")), + stderr=subprocess.STDOUT, + shell=True + ) except subprocess.CalledProcessError, e: - help = e.output - for l in str(help).strip().split("\n"): + help_string = e.output + for l in str(help_string).strip().split("\n"): l = l.rstrip() if l == '': outtext.write("\n") else: # `docker --help` tells the user the path they called it with l = re.sub(docker_cmd, "docker", l) - outtext.write(" "+l+"\n") + outtext.write(" {}\n".format(l)) outtext.write("\n") + # TODO: look for an complain about any missing commands def update_cli_reference(): originalFile = "docs/sources/reference/commandline/cli.md" os.rename(originalFile, originalFile+".bak") - intext = open(originalFile+".bak", "r") + intext = open("{}.bak".format(originalFile), "r") outtext = open(originalFile, "w") mode = 'p' @@ -49,7 +52,7 @@ def update_cli_reference(): command = "" # 2 mode line-by line parser for line in intext: - if mode=='p': + if mode == 'p': # Prose match = re.match("( \s*)Usage: docker ([a-z]+)", line) if match: @@ -69,31 +72,47 @@ def update_cli_reference(): else: # command usage block match = re.match("("+space+")(.*)|^$", line) - #print "CMD ", command if not match: - # The end of the current usage block - Shell out to run docker to see the new output + # The end of the current usage block + # Shell out to run docker to see the new output print_usage(outtext, docker_cmd, command) outtext.write(line) mode = 'p' if mode == 'c': print_usage(outtext, docker_cmd, command) + def update_man_pages(): cmds = [] try: - help = subprocess.check_output("".join((docker_cmd)), stderr=subprocess.STDOUT, shell=True) + help_string = subprocess.check_output( + "".join((docker_cmd)), + stderr=subprocess.STDOUT, + shell=True + ) except subprocess.CalledProcessError, e: - help = e.output - for l in str(help).strip().split("\n"): + help_string = e.output + for l in str(help_string).strip().split("\n"): l = l.rstrip() if l != "": match = re.match(" (.*?) .*", l) if match: cmds.append(match.group(1)) - desc_re = re.compile(r".*# DESCRIPTION(.*?)# (OPTIONS|EXAMPLES?).*", re.MULTILINE|re.DOTALL) - example_re = re.compile(r".*# EXAMPLES?(.*)# HISTORY.*", re.MULTILINE|re.DOTALL) - history_re = re.compile(r".*# HISTORY(.*)", re.MULTILINE|re.DOTALL) + desc_re = re.compile( + r".*# DESCRIPTION(.*?)# (OPTIONS|EXAMPLES?).*", + re.MULTILINE | re.DOTALL + ) + + example_re = re.compile( + r".*# EXAMPLES?(.*)# HISTORY.*", + re.MULTILINE | re.DOTALL + ) + + history_re = re.compile( + r".*# HISTORY(.*)", + re.MULTILINE | re.DOTALL + ) for command in cmds: print "COMMAND: "+command @@ -113,41 +132,43 @@ def update_man_pages(): match = history_re.match(txt) if match: history = match.group(1).strip() - + usage = "" usage_description = "" params = {} key_params = {} - - help = "" + try: - help = subprocess.check_output("".join((docker_cmd, " ", command, " --help")), stderr=subprocess.STDOUT, shell=True) + help_string = subprocess.check_output( + "".join((docker_cmd, " ", command, " --help")), + stderr=subprocess.STDOUT, + shell=True + ) except subprocess.CalledProcessError, e: - help = e.output + help_string = e.output + last_key = "" for l in str(help).split("\n"): l = l.rstrip() if l != "": - match = re.match("Usage: docker "+command+"(.*)", l) + match = re.match("Usage: docker {}(.*)".format(command), l) if match: usage = match.group(1).strip() else: - #print ">>>>"+l match = re.match(" (-+)(.*) \s+(.*)", l) if match: last_key = match.group(2).rstrip() - #print " found "+match.group(1) key_params[last_key] = match.group(1)+last_key params[last_key] = match.group(3) else: if last_key != "": - params[last_key] = params[last_key] + "\n" + l + params[last_key] = "{}\n{}".format(params[last_key], l) else: if usage_description != "": usage_description = usage_description + "\n" usage_description = usage_description + l - - # replace [OPTIONS] with the list of params + + # replace [OPTIONS] with the list of params options = "" match = re.match("\[OPTIONS\](.*)", usage) if match: @@ -160,57 +181,57 @@ def update_man_pages(): ps = [] opts = [] for k in key_params[key].split(","): - #print "......"+k match = re.match("(-+)([A-Za-z-0-9]*)(?:=(.*))?", k.lstrip()) if match: - p = "**"+match.group(1)+match.group(2)+"**" - o = "**"+match.group(1)+match.group(2)+"**" + p = "**{}{}**".format(match.group(1), match.group(2)) + o = "**{}{}**".format(match.group(1), match.group(2)) if match.group(3): - # if ="" then use UPPERCASE(group(2))" val = match.group(3) if val == "\"\"": val = match.group(2).upper() - p = p+"[=*"+val+"*]" + p = "{}[=*{}*]".format(p, val) val = match.group(3) if val in ("true", "false"): params[key] = params[key].rstrip() if not params[key].endswith('.'): params[key] = params[key]+ "." - params[key] = params[key] + " The default is *"+val+"*." + params[key] = "{} The default is *{}*.".format(params[key], val) val = "*true*|*false*" - o = o+"="+val + o = "{}={}".format(o, val) ps.append(p) opts.append(o) else: - print "nomatch:"+k - new_usage = new_usage+ "\n["+"|".join(ps)+"]" - options = options + ", ".join(opts) + "\n "+ params[key]+"\n\n" + print "nomatch:{}".format(k) + new_usage = "{}\n[{}]".format(new_usage, "|".join(ps)) + options = "{}{}\n {}\n\n".format(options, ", ".join(opts), params[key]) if new_usage != "": - new_usage = new_usage.strip() + "\n" + new_usage = "{}\n".format(new_usage.strip()) usage = new_usage + usage - - - outtext = open("docs/man/docker-"+command+".1.md", "w") + + outtext = open("docs/man/docker-{}.1.md".format(command), "w") outtext.write("""% DOCKER(1) Docker User Manuals % Docker Community % JUNE 2014 # NAME """) - outtext.write("docker-"+command+" - "+usage_description+"\n\n") - outtext.write("# SYNOPSIS\n**docker "+command+"**\n"+usage+"\n\n") + outtext.write("docker-{} - {}\n\n".format(command, usage_description)) + outtext.write("# SYNOPSIS\n**docker {}**\n{}\n\n".format(command, usage)) if description != "": - outtext.write("# DESCRIPTION"+description) + outtext.write("# DESCRIPTION{}".format(description)) if options == "": options = "There are no available options.\n\n" - outtext.write("# OPTIONS\n"+options) + outtext.write("# OPTIONS\n{}".format(options)) if examples != "": - outtext.write("# EXAMPLES"+examples) + outtext.write("# EXAMPLES{}".format(examples)) outtext.write("# HISTORY\n") if history != "": - outtext.write(history+"\n") - recent_history_re = re.compile(".*"+date_string+".*", re.MULTILINE|re.DOTALL) + outtext.write("{}\n".format(history)) + recent_history_re = re.compile( + ".*{}.*".format(date_string), + re.MULTILINE | re.DOTALL + ) if not recent_history_re.match(history): - outtext.write(date_string+", updated by Sven Dowideit \n") + outtext.write("{}, updated by Sven Dowideit \n".format(date_string)) outtext.close() # main diff --git a/docs/man/Dockerfile.5.md b/docs/man/Dockerfile.5.md index 9772d4e114..4104dc2321 100644 --- a/docs/man/Dockerfile.5.md +++ b/docs/man/Dockerfile.5.md @@ -97,7 +97,7 @@ or **FROM ubuntu** **CMD echo "This is a test." | wc -** If you run without a shell, then you must express the command as a - JSON arry and give the full path to the executable. This array form is the + JSON array and give the full path to the executable. This array form is the preferred form of CMD. All additional parameters must be individually expressed as strings in the array: **FROM ubuntu** @@ -131,12 +131,13 @@ or interactively, as with the following command: **docker run -t -i image bash** **ADD** - --**ADD ** The ADD instruction copies new files from and adds them - to the filesystem of the container at path . must be the path to a - file or directory relative to the source directory that is being built (the - context of the build) or a remote file URL. is the absolute path to - which the source is copied inside the target container. All new files and - directories are created with mode 0755, with uid and gid 0. + --**ADD ... ** The ADD instruction copies new files, directories + or remote file URLs to the filesystem of the container at path . + Mutliple resources may be specified but if they are files or directories + then they must be relative to the source directory that is being built + (the context of the build). is the absolute path to + which the source is copied inside the target container. All new files and + directories are created with mode 0755, with uid and gid 0. **ENTRYPOINT** --**ENTRYPOINT** has two forms: ENTRYPOINT ["executable", "param1", "param2"] @@ -177,7 +178,7 @@ or -- **WORKDIR /path/to/workdir** The WORKDIR instruction sets the working directory for the **RUN**, **CMD**, and **ENTRYPOINT** Dockerfile commands that follow it. It can be used multiple times in a single Dockerfile. Relative paths are defined relative to the path of the previous **WORKDIR** instruction. For example: - **WORKDIR /a WORKDIR /b WORKDIR c RUN pwd** + **WORKDIR /a WORKDIR b WORKDIR c RUN pwd** In the above example, the output of the **pwd** command is **a/b/c**. **ONBUILD** diff --git a/docs/man/docker-create.1.md b/docs/man/docker-create.1.md new file mode 100644 index 0000000000..c5ed0349c4 --- /dev/null +++ b/docs/man/docker-create.1.md @@ -0,0 +1,140 @@ +% DOCKER(1) Docker User Manuals +% Docker Community +% JUNE 2014 +# NAME +docker-create - Create a new container + +# SYNOPSIS +**docker create** +[**-a**|**--attach**[=*[]*]] +[**--add-host**[=*[]*]] +[**-c**|**--cpu-shares**[=*0*]] +[**--cap-add**[=*[]*]] +[**--cap-drop**[=*[]*]] +[**--cidfile**[=*CIDFILE*]] +[**--cpuset**[=*CPUSET*]] +[**--device**[=*[]*]] +[**--dns-search**[=*[]*]] +[**--dns**[=*[]*]] +[**-e**|**--env**[=*[]*]] +[**--entrypoint**[=*ENTRYPOINT*]] +[**--env-file**[=*[]*]] +[**--expose**[=*[]*]] +[**-h**|**--hostname**[=*HOSTNAME*]] +[**-i**|**--interactive**[=*false*]] +[**--link**[=*[]*]] +[**--lxc-conf**[=*[]*]] +[**-m**|**--memory**[=*MEMORY*]] +[**--name**[=*NAME*]] +[**--net**[=*"bridge"*]] +[**-P**|**--publish-all**[=*false*]] +[**-p**|**--publish**[=*[]*]] +[**--privileged**[=*false*]] +[**--restart**[=*RESTART*]] +[**-t**|**--tty**[=*false*]] +[**-u**|**--user**[=*USER*]] +[**-v**|**--volume**[=*[]*]] +[**--volumes-from**[=*[]*]] +[**-w**|**--workdir**[=*WORKDIR*]] + IMAGE [COMMAND] [ARG...] + +# OPTIONS +**-a**, **--attach**=[] + Attach to STDIN, STDOUT or STDERR. + +**--add-host**=[] + Add a custom host-to-IP mapping (host:ip) + +**-c**, **--cpu-shares**=0 + CPU shares (relative weight) + +**--cap-add**=[] + Add Linux capabilities + +**--cap-drop**=[] + Drop Linux capabilities + +**--cidfile**="" + Write the container ID to the file + +**--cpuset**="" + CPUs in which to allow execution (0-3, 0,1) + +**--device**=[] + Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc) + +**--dns-search**=[] + Set custom DNS search domains + +**--dns**=[] + Set custom DNS servers + +**-e**, **--env**=[] + Set environment variables + +**--entrypoint**="" + Overwrite the default ENTRYPOINT of the image + +**--env-file**=[] + Read in a line delimited file of environment variables + +**--expose**=[] + Expose a port from the container without publishing it to your host + +**-h**, **--hostname**="" + Container host name + +**-i**, **--interactive**=*true*|*false* + Keep STDIN open even if not attached. The default is *false*. + +**--link**=[] + Add link to another container in the form of name:alias + +**--lxc-conf**=[] + (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" + +**-m**, **--memory**="" + Memory limit (format: , where unit = b, k, m or g) + +**--name**="" + Assign a name to the container + +**--net**="bridge" + Set the Network mode for the container + 'bridge': creates a new network stack for the container on the docker bridge + 'none': no networking for this container + 'container:': reuses another container network stack + 'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. + +**-P**, **--publish-all**=*true*|*false* + Publish all exposed ports to the host interfaces. The default is *false*. + +**-p**, **--publish**=[] + Publish a container's port to the host + format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort + (use 'docker port' to see the actual mapping) + +**--privileged**=*true*|*false* + Give extended privileges to this container. The default is *false*. + +**--restart**="" + Restart policy to apply when a container exits (no, on-failure[:max-retry], always) + +**-t**, **--tty**=*true*|*false* + Allocate a pseudo-TTY. The default is *false*. + +**-u**, **--user**="" + Username or UID + +**-v**, **--volume**=[] + Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container) + +**--volumes-from**=[] + Mount volumes from the specified container(s) + +**-w**, **--workdir**="" + Working directory inside the container + +# HISTORY +August 2014, updated by Sven Dowideit +September 2014, updated by Sven Dowideit diff --git a/docs/man/docker-events.1.md b/docs/man/docker-events.1.md index 8fa85871a8..c888439701 100644 --- a/docs/man/docker-events.1.md +++ b/docs/man/docker-events.1.md @@ -14,6 +14,14 @@ docker-events - Get real time events from the server Get event information from the Docker daemon. Information can include historical information and real-time information. +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + # OPTIONS **--since**="" Show all events created since timestamp diff --git a/docs/man/docker-exec.1.md b/docs/man/docker-exec.1.md new file mode 100644 index 0000000000..d5ec1265bd --- /dev/null +++ b/docs/man/docker-exec.1.md @@ -0,0 +1,29 @@ +% DOCKER(1) Docker User Manuals +% Docker Community +% SEPT 2014 +# NAME +docker-exec - Run a command in a running container + +# SYNOPSIS +**docker exec** +[**-d**|**--detach**[=*false*]] +[**-i**|**--interactive**[=*false*]] +[**-t**|**--tty**[=*false*]] + CONTAINER COMMAND [ARG...] + +# DESCRIPTION + +Run a process in a running container. + +# Options + +**-d**, **--detach**=*true*|*false* + Detached mode. This runs the new process in the background. + +**-i**, **--interactive**=*true*|*false* + When set to true, keep STDIN open even if not attached. The default is false. + +**-t**, **--tty**=*true*|*false* + When set to true Docker can allocate a pseudo-tty and attach to the standard +input of the process. This can be used, for example, to run a throwaway +interactive shell. The default value is false. diff --git a/docs/man/docker-port.1.md b/docs/man/docker-port.1.md index 07b84b12d9..97cc61b7e5 100644 --- a/docs/man/docker-port.1.md +++ b/docs/man/docker-port.1.md @@ -2,14 +2,30 @@ % Docker Community % JUNE 2014 # NAME -docker-port - Lookup the public-facing port that is NAT-ed to PRIVATE_PORT +docker-port - List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT # SYNOPSIS -**docker port** -CONTAINER PRIVATE_PORT +**docker port** CONTAINER [PRIVATE_PORT[/PROTO]] -# OPTIONS -There are no available options. +# DESCRIPTION +List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT + +# EXAMPLES +You can find out all the ports mapped by not specifying a `PRIVATE_PORT`, or +ask for just a specific mapping: + + $ docker ps test + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + b650456536c7 busybox:latest top 54 minutes ago Up 54 minutes 0.0.0.0:1234->9876/tcp, 0.0.0.0:4321->7890/tcp test + $ docker port test + 7890/tcp -> 0.0.0.0:4321 + 9876/tcp -> 0.0.0.0:1234 + $ docker port test 7890/tcp + 0.0.0.0:4321 + $ docker port test 7890/udp + 2014/06/24 11:53:36 Error: No public port '7890/udp' published for test + $ docker port test 7890 + 0.0.0.0:4321 # HISTORY April 2014, Originally compiled by William Henry (whenry at redhat dot com) diff --git a/docs/man/docker-pull.1.md b/docs/man/docker-pull.1.md index 465c97aadd..01c664f56f 100644 --- a/docs/man/docker-pull.1.md +++ b/docs/man/docker-pull.1.md @@ -6,6 +6,7 @@ docker-pull - Pull an image or a repository from the registry # SYNOPSIS **docker pull** +[**-a**|**--all-tags**[=*false*]] NAME[:TAG] # DESCRIPTION @@ -16,11 +17,14 @@ images for that repository name are pulled down including any tags. It is also possible to specify a non-default registry to pull from. # OPTIONS -There are no available options. +**-a**, **--all-tags**=*true*|*false* + Download all tagged images in the repository. The default is *false*. # EXAMPLES # Pull a repository with multiple images +# Note that if the image is previously downloaded then the status would be +# 'Status: Image is up to date for fedora' $ sudo docker pull fedora Pulling repository fedora @@ -29,6 +33,8 @@ There are no available options. 511136ea3c5a: Download complete 73bd853d2ea5: Download complete + Status: Downloaded newer image for fedora + $ sudo docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE fedora rawhide ad57ef8d78d7 5 days ago 359.3 MB @@ -37,6 +43,8 @@ There are no available options. fedora latest 105182bb5e8b 5 days ago 372.7 MB # Pull an image, manually specifying path to the registry and tag +# Note that if the image is previously downloaded then the status would be +# 'Status: Image is up to date for registry.hub.docker.com/fedora:20' $ sudo docker pull registry.hub.docker.com/fedora:20 Pulling repository fedora @@ -44,6 +52,8 @@ There are no available options. 511136ea3c5a: Download complete fd241224e9cf: Download complete + Status: Downloaded newer image for registry.hub.docker.com/fedora:20 + $ sudo docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE fedora 20 3f2fed40e4b0 4 days ago 372.7 MB @@ -53,3 +63,4 @@ There are no available options. April 2014, Originally compiled by William Henry (whenry at redhat dot com) based on docker.com source material and internal work. June 2014, updated by Sven Dowideit +August 2014, updated by Sven Dowideit diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index 225fb78cb8..e3d846749d 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -7,6 +7,7 @@ docker-run - Run a command in a new container # SYNOPSIS **docker run** [**-a**|**--attach**[=*[]*]] +[**--add-host**[=*[]*]] [**-c**|**--cpu-shares**[=*0*]] [**--cap-add**[=*[]*]] [**--cap-drop**[=*[]*]] @@ -22,6 +23,7 @@ docker-run - Run a command in a new container [**--expose**[=*[]*]] [**-h**|**--hostname**[=*HOSTNAME*]] [**-i**|**--interactive**[=*false*]] +[**--security-opt**[=*[]*]] [**--link**[=*[]*]] [**--lxc-conf**[=*[]*]] [**-m**|**--memory**[=*MEMORY*]] @@ -64,6 +66,10 @@ error. It can even pretend to be a TTY (this is what most commandline executables expect) and pass along signals. The **-a** option can be set for each of stdin, stdout, and stderr. +**--add-host**=*hostname*:*ip* + Add a line to /etc/hosts. The format is hostname:ip. The **--add-host** +option can be set multiple times. + **-c**, **--cpu-shares**=0 CPU shares in relative weight. You can increase the priority of a container with the -c option. By default, all containers run at the same priority and get @@ -138,6 +144,13 @@ container can be started with the **--link**. **-i**, **--interactive**=*true*|*false* When set to true, keep stdin open even if not attached. The default is false. +**--security-opt**=*secdriver*:*name*:*value* + "label:user:USER" : Set the label user for the container + "label:role:ROLE" : Set the label role for the container + "label:type:TYPE" : Set the label type for the container + "label:level:LEVEL" : Set the label level for the container + "label:disable" : Turn off label confinement for the container + **--link**=*name*:*alias* Add link to another container. The format is name:alias. If the operator uses **--link** when starting the new client container, then the client @@ -181,12 +194,13 @@ and foreground Docker containers. When set to true publish all exposed ports to the host interfaces. The default is false. If the operator uses -P (or -p) then Docker will make the exposed port accessible on the host and the ports will be available to any -client that can reach the host. To find the map between the host ports and the -exposed ports, use **docker port**. +client that can reach the host. When using -P, Docker will bind the exposed +ports to a random port on the host between 49153 and 65535. To find the +mapping between the host ports and the exposed ports, use **docker port**. **-p**, **--publish**=[] Publish a container's port to the host (format: ip:hostPort:containerPort | -ip::containerPort | hostPort:containerPort) (use **docker port** to see the +ip::containerPort | hostPort:containerPort | containerPort) (use **docker port** to see the actual mapping) **--privileged**=*true*|*false* @@ -329,7 +343,7 @@ to create a secure tunnel for the parent to access. ## Mapping Ports for External Usage The exposed port of an application can be mapped to a host port using the **-p** -flag. For example a httpd port 80 can be mapped to the host port 8080 using the +flag. For example, a httpd port 80 can be mapped to the host port 8080 using the following: # docker run -p 8080:80 -d -i -t fedora/httpd @@ -377,6 +391,35 @@ to the host directory: Now, writing to the /data1 volume in the container will be allowed and the changes will also be reflected on the host in /var/db. +## Using alternative security labeling + +You can override the default labeling scheme for each container by specifying +the `--security-opt` flag. For example, you can specify the MCS/MLS level, a +requirement for MLS systems. Specifying the level in the following command +allows you to share the same content between containers. + + # docker run --security-opt label:level:s0:c100,c200 -i -t fedora bash + +An MLS example might be: + + # docker run --security-opt label:level:TopSecret -i -t rhel7 bash + +To disable the security labeling for this container versus running with the +`--permissive` flag, use the following command: + + # docker run --security-opt label:disable -i -t fedora bash + +If you want a tighter security policy on the processes within a container, +you can specify an alternate type for the container. You could run a container +that is only allowed to listen on Apache ports by executing the following +command: + + # docker run --security-opt label:type:svirt_apache_t -i -t centos bash + +Note: + +You would have to write policy defining a `svirt_apache_t` type. + # HISTORY April 2014, Originally compiled by William Henry (whenry at redhat dot com) based on docker.com source material and internal work. diff --git a/docs/man/docker-save.1.md b/docs/man/docker-save.1.md index 533b4c8435..ea78475b51 100644 --- a/docs/man/docker-save.1.md +++ b/docs/man/docker-save.1.md @@ -17,7 +17,7 @@ Stream to a file instead of STDOUT by using **-o**. # OPTIONS **-o**, **--output**="" - Write to an file, instead of STDOUT + Write to a file, instead of STDOUT # EXAMPLES diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index 3932097255..26f5c2133a 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -49,12 +49,19 @@ unix://[/path/to/socket] to use. **-g**="" Path to use as the root of the Docker runtime. Default is `/var/lib/docker`. + +**--fixed-cidr**="" + IPv4 subnet for fixed IPs (ex: 10.20.0.0/16); this subnet must be nested in the bridge subnet (which is defined by \-b or \-\-bip) + **--icc**=*true*|*false* Enable inter\-container communication. Default is true. **--ip**="" Default IP address to use when binding container ports. Default is `0.0.0.0`. +**--ip-masq**=*true*|*false* + Enable IP masquerading for bridge's IP range. Default is true. + **--iptables**=*true*|*false* Disable Docker's addition of iptables rules. Default is true. @@ -64,6 +71,9 @@ unix://[/path/to/socket] to use. **-p**="" Path to use for daemon PID file. Default is `/var/run/docker.pid` +**--registry-mirror=:// + Prepend a registry mirror to be used for image pulls. May be specified multiple times. + **-s**="" Force the Docker runtime to use a specific storage driver. @@ -86,13 +96,18 @@ unix://[/path/to/socket] to use. **docker-cp(1)** Copy files/folders from a container's filesystem to the host at path +**docker-create(1)** + Create a new container + **docker-diff(1)** Inspect changes on a container's filesystem - **docker-events(1)** Get real time events from the server +**docker-exec(1)** + Run a command in a running container + **docker-export(1)** Stream the contents of a container as a tar archive diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index c45b717d91..5ea8d56e60 100755 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -34,6 +34,7 @@ pages: - ['installation/mac.md', 'Installation', 'Mac OS X'] - ['installation/ubuntulinux.md', 'Installation', 'Ubuntu'] - ['installation/rhel.md', 'Installation', 'Red Hat Enterprise Linux'] +- ['installation/oracle.md', 'Installation', 'Oracle Linux'] - ['installation/centos.md', 'Installation', 'CentOS'] - ['installation/debian.md', 'Installation', 'Debian'] - ['installation/gentoolinux.md', 'Installation', 'Gentoo'] @@ -58,12 +59,15 @@ pages: - ['userguide/dockerlinks.md', 'User Guide', 'Linking containers together' ] - ['userguide/dockervolumes.md', 'User Guide', 'Managing data in containers' ] - ['userguide/dockerrepos.md', 'User Guide', 'Working with Docker Hub' ] +- ['userguide/level1.md', '**HIDDEN**' ] +- ['userguide/level2.md', '**HIDDEN**' ] # Docker Hub docs: - ['docker-hub/index.md', 'Docker Hub', 'Docker Hub' ] - ['docker-hub/accounts.md', 'Docker Hub', 'Accounts'] - ['docker-hub/repos.md', 'Docker Hub', 'Repositories'] - ['docker-hub/builds.md', 'Docker Hub', 'Automated Builds'] +- ['docker-hub/official_repos.md', 'Docker Hub', 'Official Repo Guidelines'] # Examples: - ['examples/index.md', '**HIDDEN**'] @@ -82,16 +86,18 @@ pages: - ['articles/networking.md', 'Articles', 'Advanced networking'] - ['articles/security.md', 'Articles', 'Security'] - ['articles/https.md', 'Articles', 'Running Docker with HTTPS'] -- ['articles/host_integration.md', 'Articles', 'Automatically starting Containers'] +- ['articles/host_integration.md', 'Articles', 'Automatically starting containers'] +- ['articles/baseimages.md', 'Articles', 'Creating a base image'] +- ['articles/dockerfile_best-practices.md', 'Articles', 'Best practices for writing Dockerfiles'] - ['articles/certificates.md', 'Articles', 'Using certificates for repository client verification'] - ['articles/using_supervisord.md', 'Articles', 'Using Supervisor'] - ['articles/cfengine_process_management.md', 'Articles', 'Process management with CFEngine'] - ['articles/puppet.md', 'Articles', 'Using Puppet'] - ['articles/chef.md', 'Articles', 'Using Chef'] - ['articles/dsc.md', 'Articles', 'Using PowerShell DSC'] -- ['articles/ambassador_pattern_linking.md', 'Articles', 'Cross-Host linking using Ambassador Containers'] +- ['articles/ambassador_pattern_linking.md', 'Articles', 'Cross-Host linking using ambassador containers'] - ['articles/runmetrics.md', 'Articles', 'Runtime metrics'] -- ['articles/baseimages.md', 'Articles', 'Creating a Base Image'] +- ['articles/b2d_volume_resize.md', 'Articles', 'Increasing a Boot2Docker volume'] # Reference - ['reference/index.md', '**HIDDEN**'] @@ -103,8 +109,10 @@ pages: - ['reference/api/index.md', '**HIDDEN**'] - ['reference/api/docker-io_api.md', 'Reference', 'Docker Hub API'] - ['reference/api/registry_api.md', 'Reference', 'Docker Registry API'] +- ['reference/api/registry_api_client_libraries.md', 'Reference', 'Docker Registry API Client Libraries'] - ['reference/api/hub_registry_spec.md', 'Reference', 'Docker Hub and Registry Spec'] - ['reference/api/docker_remote_api.md', 'Reference', 'Docker Remote API'] +- ['reference/api/docker_remote_api_v1.15.md', 'Reference', 'Docker Remote API v1.15'] - ['reference/api/docker_remote_api_v1.14.md', 'Reference', 'Docker Remote API v1.14'] - ['reference/api/docker_remote_api_v1.13.md', 'Reference', 'Docker Remote API v1.13'] - ['reference/api/docker_remote_api_v1.12.md', 'Reference', 'Docker Remote API v1.12'] diff --git a/docs/release.sh b/docs/release.sh index ba309aaea9..cdb1a94c82 100755 --- a/docs/release.sh +++ b/docs/release.sh @@ -9,6 +9,8 @@ To publish the Docker documentation you need to set your access_key and secret_k (with the keys in a [profile $AWS_S3_BUCKET] section - so you can have more than one set of keys in your file) and set the AWS_S3_BUCKET env var to the name of your bucket. +If you're publishing the current release's documentation, also set `BUILD_ROOT=yes` + make AWS_S3_BUCKET=docs-stage.docker.com docs-release will then push the documentation site to your s3 bucket. @@ -25,6 +27,16 @@ if [ "$$AWS_S3_BUCKET" == "docs.docker.com" ]; then echo "Please do not push '-dev' documentation to docs.docker.com ($VERSION)" exit 1 fi + cat > ./sources/robots.txt <<'EOF' +User-agent: * +Allow: / +EOF + +else + cat > ./sources/robots.txt <<'EOF' +User-agent: * +Disallow: / +EOF fi # Remove the last version - 1.0.2-dev -> 1.0 @@ -74,7 +86,7 @@ upload_current_documentation() { # a really complicated way to send only the files we want # if there are too many in any one set, aws s3 sync seems to fall over with 2 files to go # versions.html_fragment - endings=( json html xml css js gif png JPG ttf svg woff html_fragment ) + endings=( json txt html xml css js gif png JPG ttf svg woff html_fragment ) for i in ${endings[@]}; do include="" for j in ${endings[@]}; do @@ -82,11 +94,12 @@ upload_current_documentation() { include="$include --exclude *.$j" fi done + include="--include *.$i $include" echo "uploading *.$i" run="aws s3 sync --profile $BUCKET --cache-control \"max-age=3600\" --acl public-read \ $include \ - --exclude *.txt \ --exclude *.text* \ + --exclude *.*~ \ --exclude *Dockerfile \ --exclude *.DS_Store \ --exclude *.psd \ diff --git a/docs/sources/articles.md b/docs/sources/articles.md index 51335c6afd..37f2cd80f1 100644 --- a/docs/sources/articles.md +++ b/docs/sources/articles.md @@ -11,3 +11,5 @@ - [Runtime Metrics](runmetrics/) - [Automatically Start Containers](host_integration/) - [Link via an Ambassador Container](ambassador_pattern_linking/) + - [Increase a Boot2Docker Volume](b2d_volume_resize/) + - [Run a Local Registry Mirror](registry_mirror/) diff --git a/docs/sources/articles/ambassador_pattern_linking.md b/docs/sources/articles/ambassador_pattern_linking.md index 755fa4dc9c..9b1b0329a6 100644 --- a/docs/sources/articles/ambassador_pattern_linking.md +++ b/docs/sources/articles/ambassador_pattern_linking.md @@ -34,23 +34,23 @@ controlled entirely from the `docker run` parameters. Start actual Redis server on one Docker host - big-server $ docker run -d --name redis crosbymichael/redis + big-server $ sudo docker run -d --name redis crosbymichael/redis Then add an ambassador linked to the Redis server, mapping a port to the outside world - big-server $ docker run -d --link redis:redis --name redis_ambassador -p 6379:6379 svendowideit/ambassador + big-server $ sudo docker run -d --link redis:redis --name redis_ambassador -p 6379:6379 svendowideit/ambassador On the other host, you can set up another ambassador setting environment variables for each remote port we want to proxy to the `big-server` - client-server $ docker run -d --name redis_ambassador --expose 6379 -e REDIS_PORT_6379_TCP=tcp://192.168.1.52:6379 svendowideit/ambassador + client-server $ sudo docker run -d --name redis_ambassador --expose 6379 -e REDIS_PORT_6379_TCP=tcp://192.168.1.52:6379 svendowideit/ambassador Then on the `client-server` host, you can use a Redis client container to talk to the remote Redis server, just by linking to the local Redis ambassador. - client-server $ docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli + client-server $ sudo docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli redis 172.17.0.160:6379> ping PONG @@ -62,19 +62,19 @@ does automatically (with a tiny amount of `sed`) On the Docker host (192.168.1.52) that Redis will run on: # start actual redis server - $ docker run -d --name redis crosbymichael/redis + $ sudo docker run -d --name redis crosbymichael/redis # get a redis-cli container for connection testing - $ docker pull relateiq/redis-cli + $ sudo docker pull relateiq/redis-cli # test the redis server by talking to it directly - $ docker run -t -i --rm --link redis:redis relateiq/redis-cli + $ sudo docker run -t -i --rm --link redis:redis relateiq/redis-cli redis 172.17.0.136:6379> ping PONG ^D # add redis ambassador - $ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 busybox sh + $ sudo docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 busybox sh In the `redis_ambassador` container, you can see the linked Redis containers `env`: @@ -96,9 +96,9 @@ containers `env`: This environment is used by the ambassador `socat` script to expose Redis to the world (via the `-p 6379:6379` port mapping): - $ docker rm redis_ambassador + $ sudo docker rm redis_ambassador $ sudo ./contrib/mkimage-unittest.sh - $ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 docker-ut sh + $ sudo docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 docker-ut sh $ socat TCP4-LISTEN:6379,fork,reuseaddr TCP4:172.17.0.136:6379 @@ -107,14 +107,14 @@ Now ping the Redis server via the ambassador: Now go to a different server: $ sudo ./contrib/mkimage-unittest.sh - $ docker run -t -i --expose 6379 --name redis_ambassador docker-ut sh + $ sudo docker run -t -i --expose 6379 --name redis_ambassador docker-ut sh $ socat TCP4-LISTEN:6379,fork,reuseaddr TCP4:192.168.1.52:6379 And get the `redis-cli` image so we can talk over the ambassador bridge. - $ docker pull relateiq/redis-cli - $ docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli + $ sudo docker pull relateiq/redis-cli + $ sudo docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli redis 172.17.0.160:6379> ping PONG diff --git a/docs/sources/articles/b2d_volume_images/add_cd.png b/docs/sources/articles/b2d_volume_images/add_cd.png new file mode 100644 index 0000000000..50d7c4e7ed Binary files /dev/null and b/docs/sources/articles/b2d_volume_images/add_cd.png differ diff --git a/docs/sources/articles/b2d_volume_images/add_new_controller.png b/docs/sources/articles/b2d_volume_images/add_new_controller.png new file mode 100644 index 0000000000..791a310585 Binary files /dev/null and b/docs/sources/articles/b2d_volume_images/add_new_controller.png differ diff --git a/docs/sources/articles/b2d_volume_images/add_volume.png b/docs/sources/articles/b2d_volume_images/add_volume.png new file mode 100644 index 0000000000..ea2d6f6e79 Binary files /dev/null and b/docs/sources/articles/b2d_volume_images/add_volume.png differ diff --git a/docs/sources/articles/b2d_volume_images/boot_order.png b/docs/sources/articles/b2d_volume_images/boot_order.png new file mode 100644 index 0000000000..a62cc95e5a Binary files /dev/null and b/docs/sources/articles/b2d_volume_images/boot_order.png differ diff --git a/docs/sources/articles/b2d_volume_images/gparted.png b/docs/sources/articles/b2d_volume_images/gparted.png new file mode 100644 index 0000000000..1a50155ce2 Binary files /dev/null and b/docs/sources/articles/b2d_volume_images/gparted.png differ diff --git a/docs/sources/articles/b2d_volume_images/gparted2.png b/docs/sources/articles/b2d_volume_images/gparted2.png new file mode 100644 index 0000000000..e48e9f1531 Binary files /dev/null and b/docs/sources/articles/b2d_volume_images/gparted2.png differ diff --git a/docs/sources/articles/b2d_volume_images/verify.png b/docs/sources/articles/b2d_volume_images/verify.png new file mode 100644 index 0000000000..843fa126bf Binary files /dev/null and b/docs/sources/articles/b2d_volume_images/verify.png differ diff --git a/docs/sources/articles/b2d_volume_resize.md b/docs/sources/articles/b2d_volume_resize.md new file mode 100644 index 0000000000..7d6790965e --- /dev/null +++ b/docs/sources/articles/b2d_volume_resize.md @@ -0,0 +1,101 @@ +page_title: Resizing a Boot2Docker Volume +page_description: Resizing a Boot2Docker Volume in VirtualBox with GParted +page_keywords: boot2docker, volume, virtualbox + +# Getting “no space left on device” errors with Boot2Docker? + +If you're using Boot2Docker with a large number of images, or the images you're +working with are very large, your pulls might start failing with "no space left +on device" errors when the Boot2Docker volume fills up. The solution is to +increase the volume size by first cloning it, then resizing it using a disk +partitioning tool. + +We recommend [GParted](http://gparted.sourceforge.net/download.php/index.php). +The tool comes as a bootable ISO, is a free download, and works well with +VirtualBox. + +## 1. Stop Boot2Docker + +Issue the command to stop the Boot2Docker VM on the command line: + + $ boot2docker stop + +## 2. Clone the VMDK image to a VDI image + +Boot2Docker ships with a VMDK image, which can’t be resized by VirtualBox’s +native tools. We will instead create a VDI volume and clone the VMDK volume to +it. + +Using the command line VirtualBox tools, clone the VMDK image to a VDI image: + + $ vboxmanage clonehd /full/path/to/boot2docker-hd.vmdk /full/path/to/.vdi -—format VDI -—variant Standard + +## 3. Resize the VDI volume + +Choose a size that will be appropriate for your needs. If you’re spinning up a +lot of containers, or your containers are particularly large, larger will be +better: + + $ vboxmanage modifyhd /full/path/to/.vdi —-resize + +## 4. Download a disk partitioning tool ISO + +To resize the volume, we'll use [GParted](http://gparted.sourceforge.net/download.php/). +Once you've downloaded the tool, add the ISO to the Boot2Docker VM IDE bus. +You might need to create the bus before you can add the ISO. + +> **Note:** +> It's important that you choose a partitioning tool that is available as an ISO so +> that the Boot2Docker VM can be booted with it. + + + + + + + + +


+ +## 5. Add the new VDI image + +In the settings for the Boot2Docker image in VirtualBox, remove the VMDK image +from the SATA contoller and add the VDI image. + + + +## 6. Verify the boot order + +In the **System** settings for the Boot2Docker VM, make sure that **CD/DVD** is +at the top of the **Boot Order** list. + + + +## 7. Boot to the disk partitioning ISO + +Manually start the Boot2Docker VM in VirtualBox, and the disk partitioning ISO +should start up. Using GParted, choose the **GParted Live (default settings)** +option. Choose the default keyboard, language, and XWindows settings, and the +GParted tool will start up and display the VDI volume you created. Right click +on the VDI and choose **Resize/Move**. + + + +Drag the slider representing the volume to the maximum available size, click +**Resize/Move**, and then **Apply**. + + + +Quit GParted and shut down the VM. Remove the GParted ISO from the IDE controller +for the Boot2Docker VM in VirtualBox. + +## 8. Start the Boot2Docker VM + +Fire up the Boot2Docker VM manually in VirtualBox. The VM should log in +automatically, but if it doesn't, the credentials are `docker/tcuser`. Using +the `df -h` command, verify that your changes took effect. + + + +You’re done! + diff --git a/docs/sources/articles/baseimages.md b/docs/sources/articles/baseimages.md index bc677eb8a3..3f53c8a84b 100644 --- a/docs/sources/articles/baseimages.md +++ b/docs/sources/articles/baseimages.md @@ -55,5 +55,14 @@ image to base your new minimal containers `FROM`: COPY true-asm /true CMD ["/true"] -The Dockerfile above is from extremely minimal image - [tianon/true]( +The `Dockerfile` above is from an extremely minimal image - [tianon/true]( https://github.com/tianon/dockerfiles/tree/master/true). + +## More resources + +There are lots more resources available to help you write your 'Dockerfile`. + +* There's a [complete guide to all the instructions](/reference/builder/) available for use in a `Dockerfile` in the reference section. +* To help you write a clear, readable, maintainable `Dockerfile`, we've also +written a [`Dockerfile` Best Practices guide](/articles/dockerfile_best-practices). +* If you're working on an Official Repo, be sure to check out the [Official Repo Guidelines](/docker-hub/official_repos/). diff --git a/docs/sources/articles/basics.md b/docs/sources/articles/basics.md index e931569652..8f3e1dc1aa 100644 --- a/docs/sources/articles/basics.md +++ b/docs/sources/articles/basics.md @@ -10,7 +10,7 @@ This guide assumes you have a working installation of Docker. To check your Docker install, run the following command: # Check that you have a working install - $ docker info + $ sudo docker info If you get `docker: command not found` or something like `/var/lib/docker/repositories: permission denied` you may have an @@ -126,20 +126,20 @@ TCP and a Unix socket $ JOB=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo Hello world; sleep 1; done") # Stop the container - $ docker stop $JOB + $ sudo docker stop $JOB # Start the container - $ docker start $JOB + $ sudo docker start $JOB # Restart the container - $ docker restart $JOB + $ sudo docker restart $JOB # SIGKILL a container - $ docker kill $JOB + $ sudo docker kill $JOB # Remove a container - $ docker stop $JOB # Container must be stopped to remove it - $ docker rm $JOB + $ sudo docker stop $JOB # Container must be stopped to remove it + $ sudo docker rm $JOB ## Bind a service on a TCP port diff --git a/docs/sources/articles/cfengine_process_management.md b/docs/sources/articles/cfengine_process_management.md index a9441a6d35..e32b266397 100644 --- a/docs/sources/articles/cfengine_process_management.md +++ b/docs/sources/articles/cfengine_process_management.md @@ -94,7 +94,7 @@ your image with the docker build command, e.g., Start the container with `apache2` and `sshd` running and managed, forwarding a port to our SSH instance: - $ docker run -p 127.0.0.1:222:22 -d managed_image "/usr/sbin/sshd" "/etc/init.d/apache2 start" + $ sudo docker run -p 127.0.0.1:222:22 -d managed_image "/usr/sbin/sshd" "/etc/init.d/apache2 start" We now clearly see one of the benefits of the cfe-docker integration: it allows to start several processes as part of a normal `docker run` command. diff --git a/docs/sources/articles/chef.md b/docs/sources/articles/chef.md index 5568e99afa..6ca0eba73d 100644 --- a/docs/sources/articles/chef.md +++ b/docs/sources/articles/chef.md @@ -43,7 +43,7 @@ The next step is to pull a Docker image. For this, we have a resource: This is equivalent to running: - $ docker pull samalba/docker-registry + $ sudo docker pull samalba/docker-registry There are attributes available to control how long the cookbook will allow for downloading (5 minute default). @@ -68,7 +68,7 @@ managed by Docker. This is equivalent to running the following command, but under upstart: - $ docker run --detach=true --publish='5000:5000' --env='SETTINGS_FLAVOR=local' --volume='/mnt/docker:/docker-storage' samalba/docker-registry + $ sudo docker run --detach=true --publish='5000:5000' --env='SETTINGS_FLAVOR=local' --volume='/mnt/docker:/docker-storage' samalba/docker-registry The resources will accept a single string or an array of values for any Docker flags that allow multiple values. diff --git a/docs/sources/articles/dockerfile_best-practices.md b/docs/sources/articles/dockerfile_best-practices.md new file mode 100644 index 0000000000..31f932d651 --- /dev/null +++ b/docs/sources/articles/dockerfile_best-practices.md @@ -0,0 +1,412 @@ +page_title: Best Practices for Writing Dockerfiles +page_description: Hints, tips and guidelines for writing clean, reliable Dockerfiles +page_keywords: Examples, Usage, base image, docker, documentation, dockerfile, best practices, hub, official repo + +# Best practices for writing Dockerfiles + +## Overview + +Docker can build images automatically by reading the instructions from a +`Dockerfile`, a text file that contains all the commands, in order, needed to +build a given image. `Dockerfile`s adhere to a specific format and use a +specific set of instructions. You can learn the basics on the +[Dockerfile Reference](https://docs.docker.com/reference/builder/) page. If +you’re new to writing `Dockerfile`s, you should start there. + +This document covers the best practices and methods recommended by Docker, +Inc. and the Docker community for creating easy-to-use, effective +`Dockerfile`s. We strongly suggest you follow these recommendations (in fact, +if you’re creating an Official Image, you *must* adhere to these practices). + +You can see many of these practices and recommendations in action in the [buildpack-deps `Dockerfile`](https://github.com/docker-library/buildpack-deps/blob/master/jessie/Dockerfile). + +> Note: for more detailed explanations of any of the Dockerfile commands +>mentioned here, visit the [Dockerfile Reference](https://docs.docker.com/reference/builder/) page. + +## General guidelines and recommendations + +### Containers should be ephemeral + +The container produced by the image your `Dockerfile` defines should be as +ephemeral as possible. By “ephemeral,” we mean that it can be stopped and +destroyed and a new one built and put in place with an absolute minimum of +set-up and configuration. + +### Use [a .dockerignore file](https://docs.docker.com/reference/builder/#the-dockerignore-file) + +For faster uploading and efficiency during `docker build`, you should use +a `.dockerignore` file to exclude files or directories from the build +context and final image. For example, unless`.git` is needed by your build +process or scripts, you should add it to `.dockerignore`, which can save many +megabytes worth of upload time. + +### Avoid installing unnecessary packages + +In order to reduce complexity, dependencies, file sizes, and build times, you +should avoid installing extra or unnecessary packages just because they +might be “nice to have.” For example, you don’t need to include a text editor +in a database image. + +### Run only one process per container + +In almost all cases, you should only run a single process in a single +container. Decoupling applications into multiple containers makes it much +easier to scale horizontally and reuse containers. If that service depends on +another service, make use of [container linking](https://docs.docker.com/userguide/dockerlinks/). + +### Minimize the number of layers + +You need to find the balance between readability (and thus long-term +maintainability) of the `Dockerfile` and minimizing the number of layers it +uses. Be strategic and cautious about the number of layers you use. + +### Sort multi-line arguments + +Whenever possible, ease later changes by sorting multi-line arguments +alphanumerically. This will help you avoid duplication of packages and make the +list much easier to update. This also makes PRs a lot easier to read and +review. Adding a space before a backslash (`\`) helps as well. + +Here’s an example from the [`buildpack-deps` image](https://github.com/docker-library/buildpack-deps): + + RUN apt-get update && apt-get install -y \ + bzr \ + cvs \ + git \ + mercurial \ + subversion + +### Build cache + +During the process of building an image Docker will step through the +instructions in your `Dockerfile` executing each in the order specified. +As each instruction is examined Docker will look for an existing image in its +cache that it can reuse, rather than creating a new (duplicate) image. +If you do not want to use the cache at all you can use the ` --no-cache=true` +option on the `docker build` command. + +However, if you do let Docker use its cache then it is very important to +understand when it will, and will not, find a matching image. The basic rules +that Docker will follow are outlined below: + +* Starting with a base image that is already in the cache, the next +instruction is compared against all child images derived from that base +image to see if one of them was built using the exact same instruction. If +not, the cache is invalidated. + +* In most cases simply comparing the instruction in the `Dockerfile` with one +of the child images is sufficient. However, certain instructions require +a little more examination and explanation. + +* In the case of the `ADD` and `COPY` instructions, the contents of the file(s) +being put into the image are examined. Specifically, a checksum is done +of the file(s) and then that checksum is used during the cache lookup. +If anything has changed in the file(s), including its metadata, +then the cache is invalidated. + +* Aside from the `ADD` and `COPY` commands cache checking will not look at the +files in the container to determine a cache match. For example, when processing +a `RUN apt-get -y update` command the files updated in the container +will not be examined to determine if a cache hit exists. In that case just +the command string itself will be used to find a match. + +Once the cache is invalidated, all subsequent `Dockerfile` commands will +generate new images and the cache will not be used. + + bzr \ + cvs \ + git \ + mercurial \ + subversion + +## The Dockerfile instructions + +Below you'll find recommendations for the best way to write the +various instructions available for use in a `Dockerfile`. + +### [`FROM`](https://docs.docker.com/reference/builder/#from) + +Whenever possible, use current Official Repositories as the basis for your +image. We recommend the [Debian image](https://registry.hub.docker.com/_/debian/) +since it’s very tightly controlled and kept extremely minimal (currently under +100 mb), while still being a full distribution. + +### [`RUN`](https://docs.docker.com/reference/builder/#run) + +As always, to make your `Dockerfile` more readable, understandable, and +maintainable, put long or complex `RUN` statements on multiple lines separated +with backslashes. + +Probably the most common use-case for `RUN` is an application of `apt-get`. +When using `apt-get`, here are a few things to keep in mind: + +* Don’t do `RUN apt-get update` on a single line. This will cause +caching issues if the referenced archive gets updated, which will make your +subsequent `apt-get install` fail without comment. + +* Avoid `RUN apt-get upgrade` or `dist-upgrade`, since many of the “essential” +packages from the base images will fail to upgrade inside an unprivileged +container. If a base package is out of date, you should contact its +maintainers. If you know there’s a particular package, `foo`, that needs to be +updated, use `apt-get install -y foo` and it will update automatically. + +* Do write instructions like: + + RUN apt-get update && apt-get install -y package-bar package-foo package-baz + +Writing the instruction this way not only makes it easier to read +and maintain, but also, by including `apt-get update`, ensures that the cache +will naturally be busted and the latest versions will be installed with no +further coding or manual intervention required. + +* Further natural cache-busting can be realized by version-pinning packages +(e.g., `package-foo=1.3.*`). This will force retrieval of that version +regardless of what’s in the cache. +Writing your `apt-get` code this way will greatly ease maintenance and reduce +failures due to unanticipated changes in required packages. + +#### Example + +Below is a well-formed `RUN` instruction that demonstrates the above +recommendations. Note that the last package, `s3cmd`, specifies a version +`1.1.0*`. If the image previously used an older version, specifying the new one +will cause a cache bust of `apt-get update` and ensure the installation of +the new version (which in this case had a new, required feature). + + RUN apt-get update && apt-get install -y \ + aufs-tools \ + automake \ + btrfs-tools \ + build-essential \ + curl \ + dpkg-sig \ + git \ + iptables \ + libapparmor-dev \ + libcap-dev \ + libsqlite3-dev \ + lxc=1.0* \ + mercurial \ + parallel \ + reprepro \ + ruby1.9.1 \ + ruby1.9.1-dev \ + s3cmd=1.1.0* + +Writing the instruction this way also helps you avoid potential duplication of +a given package because it is much easier to read than an instruction like: + + RUN apt-get install -y package-foo && apt-get install -y package-bar + +### [`CMD`](https://docs.docker.com/reference/builder/#cmd) + +The `CMD` instruction should be used to run the software contained by your +image, along with any arguments. `CMD` should almost always be used in the +form of `CMD [“executable”, “param1”, “param2”…]`. Thus, if the image is for a +service (Apache, Rails, etc.), you would run something like +`CMD ["apache2","-DFOREGROUND"]`. Indeed, this form of the instruction is +recommended for any service-based image. + +In most other cases, `CMD` should be given an interactive shell (bash, python, +perl, etc), for example, `CMD ["perl", "-de0"]`, `CMD ["python"]`, or +`CMD [“php”, “-a”]`. Using this form means that when you execute something like +`docker run -it python`, you’ll get dropped into a usable shell, ready to go. +`CMD` should rarely be used in the manner of `CMD [“param”, “param”]` in +conjunction with [`ENTRYPOINT`](https://docs.docker.com/reference/builder/#entrypoint), unless +you and your expected users are already quite familiar with how `ENTRYPOINT` +works. + +### [`EXPOSE`](https://docs.docker.com/reference/builder/#expose) + +The `EXPOSE` instruction indicates the ports on which a container will listen +for connections. Consequently, you should use the common, traditional port for +your application. For example, an image containing the Apache web server would +use `EXPOSE 80`, while an image containing MongoDB would use `EXPOSE 27017` and +so on. + +For external access, your users can execute `docker run` with a flag indicating +how to map the specified port to the port of their choice. +For container linking, Docker provides environment variables for the path from +the recipient container back to the source (ie, `MYSQL_PORT_3306_TCP`). + +### [`ENV`](https://docs.docker.com/reference/builder/#env) + +In order to make new software easier to run, you can use `ENV` to update the +`PATH` environment variable for the software your container installs. For +example, `ENV PATH /usr/local/nginx/bin:$PATH` will ensure that `CMD [“nginx”]` +just works. + +The `ENV` instruction is also useful for providing required environment +variables specific to services you wish to containerize, such as Postgres’s +`PGDATA`. + +Lastly, `ENV` can also be used to set commonly used version numbers so that +version bumps are easier to maintain, as seen in the following example: + + ENV PG_MAJOR 9.3 + ENV PG_VERSION 9.3.4 + RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && … + ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH + +Similar to having constant variables in a program (as opposed to hard-coding +values), this approach lets you change a single `ENV` instruction to +auto-magically bump the version of the software in your container. + +### [`ADD`](https://docs.docker.com/reference/builder/#add) or [`COPY`](https://docs.docker.com/reference/builder/#copy) + +Although `ADD` and `COPY` are functionally similar, generally speaking, `COPY` +is preferred. That’s because it’s more transparent than `ADD`. `COPY` only +supports the basic copying of local files into the container, while `ADD` has +some features (like local-only tar extraction and remote URL support) that are +not immediately obvious. Consequently, the best use for `ADD` is local tar file +auto-extraction into the image, as in `ADD rootfs.tar.xz /`. + +If you have multiple `Dockerfile` steps that use different files from your +context, `COPY` them individually, rather than all at once. This will ensure that +each step's build cache is only invalidated (forcing the step to be re-run) if the +specifically required files change. + +For example: + + COPY requirements.txt /tmp/ + RUN pip install /tmp/requirements.txt + COPY . /tmp/ + +Results in fewer cache invalidations for the `RUN` step, than if you put the +`COPY . /tmp/` before it. + +Because image size matters, using `ADD` to fetch packages from remote URLs is +strongly discouraged; you should use `curl` or `wget` instead. That way you can +delete the files you no longer need after they've been extracted and you won't +have to add another layer in your image. For example, you should avoid doing +things like: + + ADD http://example.com/big.tar.xz /usr/src/things/ + RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things + RUN make -C /usr/src/things all + +And instead, do something like: + + RUN mdkir -p /usr/src/things \ + && curl -SL http://example.com/big.tar.gz \ + | tar -xJC /usr/src/things \ + && make -C /usr/src/things all + +For other items (files, directories) that do not require `ADD`’s tar +auto-extraction capability, you should always use `COPY`. + +### [`ENTRYPOINT`](https://docs.docker.com/reference/builder/#entrypoint) + +The best use for `ENTRYPOINT` is as a helper script. Using `ENTRYPOINT` for +other tasks can make your code harder to understand. For example, + +....docker run -it official-image bash + +is much easier to understand than + +....docker run -it --entrypoint bash official-image -i + +This is especially true for new Docker users, who might naturally assume the +above command will work fine. In cases where an image uses `ENTRYPOINT` for +anything other than just a wrapper script, the command will fail and the +beginning user will then be forced to learn about `ENTRYPOINT` and +`--entrypoint`. + +In order to avoid a situation where commands are run without clear visibility +to the user, make sure your script ends with something like `exec "$@"`. After +the entrypoint completes, the script will transparently bootstrap the command +invoked by the user, making what has been run clear to the user (for example, +`docker run -it mysql mysqld --some --flags` will transparently run +`mysqld --some --flags` after `ENTRYPOINT` runs `initdb`). + +For example, let’s look at the `Dockerfile` for the +[Postgres Official Image](https://github.com/docker-library/postgres). +It refers to the following script: + +```bash +#!/bin/bash +set -e + +if [ "$1" = 'postgres' ]; then + chown -R postgres "$PGDATA" + + if [ -z "$(ls -A "$PGDATA")" ]; then + gosu postgres initdb + fi + + exec gosu postgres "$@" +fi + +exec "$@" +``` + +That script then gets copied into the container and run via `ENTRYPOINT` on +container startup: + + COPY ./docker-entrypoint.sh / + ENTRYPOINT ["/docker-entrypoint.sh"] + +### [`VOLUME`](https://docs.docker.com/reference/builder/#volume) + +The `VOLUME` instruction should be used to expose any database storage area, +configuration storage, or files/folders created by your docker container. You +are strongly encouraged to use `VOLUME` for any mutable and/or user-serviceable +parts of your image. + +### [`USER`](https://docs.docker.com/reference/builder/#user) + +If a service can run without privileges, use `USER` to change to a non-root +user. Start by creating the user and group in the `Dockerfile` with something +like `RUN groupadd -r postgres && useradd -r -g postgres postgres`. + +> **Note:** Users and groups in an image get a non-deterministic +> UID/GID in that the “next” UID/GID gets assigned regardless of image +> rebuilds. So, if it’s critical, you should assign an explicit UID/GID. + +You should avoid installing or using `sudo` since it has unpredictable TTY and +signal-forwarding behavior that can cause more more problems than it solves. If +you absolutely need functionality similar to `sudo` (e.g., initializing the +daemon as root but running it as non-root), you may be able to use +[“gosu”](https://github.com/tianon/gosu). + +Lastly, to reduce layers and complexity, avoid switching `USER` back +and forth frequently. + +### [`WORKDIR`](https://docs.docker.com/reference/builder/#workdir) + +For clarity and reliability, you should always use absolute paths for your +`WORKDIR`. Also, you should use `WORKDIR` instead of proliferating +instructions like `RUN cd … && do-something`, which are hard to read, +troubleshoot, and maintain. + +### [`ONBUILD`](https://docs.docker.com/reference/builder/#onbuild) + +`ONBUILD` is only useful for images that are going to be built `FROM` a given +image. For example, you would use `ONBUILD` for a language stack image that +builds arbitrary user software written in that language within the +`Dockerfile`, as you can see in [Ruby’s `ONBUILD` variants](https://github.com/docker-library/ruby/blob/master/2.1/onbuild/Dockerfile). + +Images built from `ONBUILD` should get a separate tag, for example: +`ruby:1.9-onbuild` or `ruby:2.0-onbuild`. + +Be careful when putting `ADD` or `COPY` in `ONBUILD`. The “onbuild” image will +fail catastrophically if the new build's context is missing the resource being +added. Adding a separate tag, as recommended above, will help mitigate this by +allowing the `Dockerfile` author to make a choice. + +## Examples For Official Repositories + +These Official Repos have exemplary `Dockerfile`s: + +* [Go](https://registry.hub.docker.com/_/golang/) +* [Perl](https://registry.hub.docker.com/_/perl/) +* [Hy](https://registry.hub.docker.com/_/hylang/) +* [Rails](https://registry.hub.docker.com/_/rails) + +## Additional Resources: + +* [Dockerfile Reference](https://docs.docker.com/reference/builder/#onbuild) +* [More about Base Images](https://docs.docker.com/articles/baseimages/) +* [More about Automated Builds](https://docs.docker.com/docker-hub/builds/) +* [Guidelines for Creating Official +Repositories](https://docs.docker.com/docker-hub/official_repos/) diff --git a/docs/sources/articles/dsc.md b/docs/sources/articles/dsc.md index 5e05c40c14..8d75b8f816 100644 --- a/docs/sources/articles/dsc.md +++ b/docs/sources/articles/dsc.md @@ -45,7 +45,7 @@ The Docker installation configuration is equivalent to running: ``` apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys\ 36A1D7869245C8950F966E92D8576A8BA88D21E9 -sh -c "echo deb https://get.docker.io/ubuntu docker main\ +sh -c "echo deb https://get.docker.com/ubuntu docker main\ > /etc/apt/sources.list.d/docker.list" apt-get update apt-get install lxc-docker @@ -164,4 +164,4 @@ container: ```powershell $containerProps = @{Name="web"; Image="node:latest"; Port="80:80"; ` Env="PORT=80"; Link="db:db"; Command="grunt"} -``` \ No newline at end of file +``` diff --git a/docs/sources/articles/host_integration.md b/docs/sources/articles/host_integration.md index fa442620e5..53fc2890e8 100644 --- a/docs/sources/articles/host_integration.md +++ b/docs/sources/articles/host_integration.md @@ -15,7 +15,7 @@ automatically restart your containers when the host is restarted. When you have finished setting up your image and are happy with your running container, you can then attach a process manager to manage it. -When your run `docker start -a` docker will automatically attach to the +When you run `docker start -a` docker will automatically attach to the running container, or start it if needed and forward all signals so that the process manager can detect when a container stops and correctly restart it. @@ -42,7 +42,7 @@ it: Next, we have to configure docker so that it's run with the option `-r=false`. Run the following command: - $ sudo sh -c "echo 'DOCKER_OPTS=\"-r=false\"' > /etc/default/docker" + $ sudo sh -c "echo 'DOCKER_OPTS=\"-r=false\"' >> /etc/default/docker" ## Sample systemd Script diff --git a/docs/sources/articles/https.md b/docs/sources/articles/https.md index 739b724c84..c8873bcbe4 100644 --- a/docs/sources/articles/https.md +++ b/docs/sources/articles/https.md @@ -122,7 +122,7 @@ providing a certificate trusted by our CA: To be able to connect to Docker and validate its certificate, you now need to provide your client keys, certificates and trusted CA: - $ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \ + $ sudo docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \ -H=dns-name-of-docker-host:2376 version > **Note**: @@ -139,16 +139,18 @@ need to provide your client keys, certificates and trusted CA: If you want to secure your Docker client connections by default, you can move the files to the `.docker` directory in your home directory - and set the -`DOCKER_HOST` variable as well. +`DOCKER_HOST` and `DOCKER_TLS_VERIFY` variables as well (instead of passing +`-H=tcp://:2376` and `--tlsverify` on every call). $ cp ca.pem ~/.docker/ca.pem $ cp cert.pem ~/.docker/cert.pem $ cp key.pem ~/.docker/key.pem $ export DOCKER_HOST=tcp://:2376 + $ export DOCKER_TLS_VERIFY=1 -Then you can run Docker with the `--tlsverify` option. +Docker will now connect securely by default: - $ docker --tlsverify ps + $ sudo docker ps ## Other modes @@ -175,4 +177,11 @@ if you want to store your keys in another location, you can specify that location using the environment variable `DOCKER_CERT_PATH`. $ export DOCKER_CERT_PATH=${HOME}/.docker/zone1/ - $ docker --tlsverify ps + $ sudo docker --tlsverify ps + +### Connecting to the Secure Docker port using `curl` + +To use `curl` to make test API requests, you need to use three extra command line +flags: + + $ curl --insecure --cert ~/.docker/cert.pem --key ~/.docker/key.pem https://boot2docker:2376/images/json` diff --git a/docs/sources/articles/networking.md b/docs/sources/articles/networking.md index f9aa2d26d3..59673ecf6f 100644 --- a/docs/sources/articles/networking.md +++ b/docs/sources/articles/networking.md @@ -54,6 +54,9 @@ server when it starts up, and cannot be changed once it is running: * `--bip=CIDR` — see [Customizing docker0](#docker0) + * `--fixed-cidr` — see + [Customizing docker0](#docker0) + * `-H SOCKET...` or `--host=SOCKET...` — This might sound like it would affect container networking, but it actually faces in the other direction: @@ -150,7 +153,10 @@ Four different options affect container domain name services. `CONTAINER_NAME`. This lets processes inside the new container connect to the hostname `ALIAS` without having to know its IP. The `--link=` option is discussed in more detail below, in the section - [Communication between containers](#between-containers). + [Communication between containers](#between-containers). Because + Docker may assign a different IP address to the linked containers + on restart, Docker updates the `ALIAS` entry in the `/etc/hosts` file + of the recipient containers. * `--dns=IP_ADDRESS...` — sets the IP addresses added as `server` lines to the container's `/etc/resolv.conf` file. Processes in the @@ -307,13 +313,13 @@ page. There are two approaches. First, you can supply `-P` or `--publish-all=true|false` to `docker run` which is a blanket operation that identifies every port with an `EXPOSE` line in the image's `Dockerfile` and maps it to a host port somewhere in -the range 49000–49900. This tends to be a bit inconvenient, since you +the range 49153–65535. This tends to be a bit inconvenient, since you then have to run other `docker` sub-commands to learn which external port a given service was mapped to. More convenient is the `-p SPEC` or `--publish=SPEC` option which lets you be explicit about exactly which external port on the Docker server — -which can be any port at all, not just those in the 49000–49900 block — +which can be any port at all, not just those in the 49153-65535 block — you want mapped to which port in the container. Either way, you should be able to peek at what Docker has accomplished @@ -362,17 +368,25 @@ By default, the Docker server creates and configures the host system's can pass packets back and forth between other physical or virtual network interfaces so that they behave as a single Ethernet network. -Docker configures `docker0` with an IP address and netmask so the host -machine can both receive and send packets to containers connected to the -bridge, and gives it an MTU — the *maximum transmission unit* or largest -packet length that the interface will allow — of either 1,500 bytes or -else a more specific value copied from the Docker host's interface that -supports its default route. Both are configurable at server startup: +Docker configures `docker0` with an IP address, netmask and IP +allocation range. The host machine can both receive and send packets to +containers connected to the bridge, and gives it an MTU — the *maximum +transmission unit* or largest packet length that the interface will +allow — of either 1,500 bytes or else a more specific value copied from +the Docker host's interface that supports its default route. These +options are configurable at server startup: * `--bip=CIDR` — supply a specific IP address and netmask for the `docker0` bridge, using standard CIDR notation like `192.168.1.5/24`. + * `--fixed-cidr=CIDR` — restrict the IP range from the `docker0` subnet, + using the standard CIDR notation like `172.167.1.0/28`. This range must + be and IPv4 range for fixed IPs (ex: 10.20.0.0/16) and must be a subset + of the bridge IP range (`docker0` or set using `--bridge`). For example + with `--fixed-cidr=192.168.1.0/25`, IPs for your containers will be chosen + from the first half of `192.168.1.0/24` subnet. + * `--mtu=BYTES` — override the maximum packet length on `docker0`. On Ubuntu you would add these to the `DOCKER_OPTS` setting in @@ -435,7 +449,7 @@ If you want to take Docker out of the business of creating its own Ethernet bridge entirely, you can set up your own bridge before starting Docker and use `-b BRIDGE` or `--bridge=BRIDGE` to tell Docker to use your bridge instead. If you already have Docker up and running with its -old `bridge0` still configured, you will probably want to begin by +old `docker0` still configured, you will probably want to begin by stopping the service and removing the interface: # Stopping Docker and removing docker0 diff --git a/docs/sources/articles/puppet.md b/docs/sources/articles/puppet.md index 81ae05ba56..e664d35c97 100644 --- a/docs/sources/articles/puppet.md +++ b/docs/sources/articles/puppet.md @@ -47,7 +47,7 @@ defined type which can be used like so: This is equivalent to running: - $ docker pull ubuntu + $ sudo docker pull ubuntu Note that it will only be downloaded if an image of that name does not already exist. This is downloading a large binary so on first run can @@ -71,7 +71,7 @@ managed by Docker. This is equivalent to running the following command, but under upstart: - $ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done" + $ sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done" Run also contains a number of optional parameters: diff --git a/docs/sources/articles/registry_mirror.md b/docs/sources/articles/registry_mirror.md new file mode 100644 index 0000000000..6cb2b958c3 --- /dev/null +++ b/docs/sources/articles/registry_mirror.md @@ -0,0 +1,83 @@ +page_title: Run a local registry mirror +page_description: How to set up and run a local registry mirror +page_keywords: docker, registry, mirror, examples + +# Run a local registry mirror + +## Why? + +If you have multiple instances of Docker running in your environment +(e.g., multiple physical or virtual machines, all running the Docker +daemon), each time one of them requires an image that it doesn't have +it will go out to the internet and fetch it from the public Docker +registry. By running a local registry mirror, you can keep most of the +image fetch traffic on your local network. + +## How does it work? + +The first time you request an image from your local registry mirror, +it pulls the image from the public Docker registry and stores it locally +before handing it back to you. On subsequent requests, the local registry +mirror is able to serve the image from its own storage. + +## How do I set up a local registry mirror? + +There are two steps to set up and use a local registry mirror. + +### Step 1: Configure your Docker daemons to use the local registry mirror + +You will need to pass the `--registry-mirror` option to your Docker daemon on +startup: + + docker --registry-mirror=http:// -d + +For example, if your mirror is serving on `http://10.0.0.2:5000`, you would run: + + docker --registry-mirror=http://10.0.0.2:5000 -d + +**NOTE:** +Depending on your local host setup, you may be able to add the +`--registry-mirror` options to the `DOCKER_OPTS` variable in +`/etc/defaults/docker`. + +### Step 2: Run the local registry mirror + +You will need to start a local registry mirror service. The +[`registry` image](https://registry.hub.docker.com/_/registry/) provides this +functionality. For example, to run a local registry mirror that serves on +port `5000` and mirrors the content at `registry-1.docker.io`: + + docker run -p 5000:5000 \ + -e STANDALONE=false \ + -e MIRROR_SOURCE=https://registry-1.docker.io \ + -e MIRROR_SOURCE_INDEX=https://index.docker.io registry + +## Test it out + +With your mirror running, pull an image that you haven't pulled before (using +`time` to time it): + + $ time docker pull node:latest + Pulling repository node + [...] + + real 1m14.078s + user 0m0.176s + sys 0m0.120s + +Now, remove the image from your local machine: + + $ sudo docker rmi node:latest + +Finally, re-pull the image: + + $ time docker pull node:latest + Pulling repository node + [...] + + real 0m51.376s + user 0m0.120s + sys 0m0.116s + +The second time around, the local registry mirror served the image from storage, +avoiding a trip out to the internet to refetch it. diff --git a/docs/sources/contributing/devenvironment.md b/docs/sources/contributing/devenvironment.md index 25a80af4af..ee120a79c8 100644 --- a/docs/sources/contributing/devenvironment.md +++ b/docs/sources/contributing/devenvironment.md @@ -101,8 +101,6 @@ something like this --- PASS: TestParseRepositoryTag (0.00 seconds) === RUN TestGetResolvConf --- PASS: TestGetResolvConf (0.00 seconds) - === RUN TestCheckLocalDns - --- PASS: TestCheckLocalDns (0.00 seconds) === RUN TestParseRelease --- PASS: TestParseRelease (0.00 seconds) === RUN TestDependencyGraphCircular @@ -115,7 +113,7 @@ something like this If $TESTFLAGS is set in the environment, it is passed as extra arguments to `go test`. You can use this to select certain tests to run, e.g., - $ TESTFLAGS=`-run \^TestBuild\$` make test + $ TESTFLAGS='-test.run \^TestBuild\$' make test If the output indicates "FAIL" and you see errors like this: diff --git a/docs/sources/docker-hub/builds.md b/docs/sources/docker-hub/builds.md index e3e2139f0a..7bf8b27eb2 100644 --- a/docs/sources/docker-hub/builds.md +++ b/docs/sources/docker-hub/builds.md @@ -198,7 +198,7 @@ It will also add it to the [Docker Hub](https://hub.docker.com) for the Docker community (for public repos) or approved team members/orgs (for private repos) to see on the repository page. -## README.md +### README.md If you have a `README.md` file in your repository, it will be used as the repository's full description.The build process will look for a diff --git a/docs/sources/docker-hub/groups.png b/docs/sources/docker-hub/groups.png index d2ede76137..0c6430efab 100644 Binary files a/docs/sources/docker-hub/groups.png and b/docs/sources/docker-hub/groups.png differ diff --git a/docs/sources/docker-hub/hub-images/bb_hooks.png b/docs/sources/docker-hub/hub-images/bb_hooks.png index d51cd03ac4..9efe4907f5 100644 Binary files a/docs/sources/docker-hub/hub-images/bb_hooks.png and b/docs/sources/docker-hub/hub-images/bb_hooks.png differ diff --git a/docs/sources/docker-hub/hub-images/bb_menu.png b/docs/sources/docker-hub/hub-images/bb_menu.png index 6f4a6813ef..fba1a03a5d 100644 Binary files a/docs/sources/docker-hub/hub-images/bb_menu.png and b/docs/sources/docker-hub/hub-images/bb_menu.png differ diff --git a/docs/sources/docker-hub/hub-images/bb_post-hook.png b/docs/sources/docker-hub/hub-images/bb_post-hook.png index 78c4730665..53100dbbce 100644 Binary files a/docs/sources/docker-hub/hub-images/bb_post-hook.png and b/docs/sources/docker-hub/hub-images/bb_post-hook.png differ diff --git a/docs/sources/docker-hub/hub-images/deploy_key.png b/docs/sources/docker-hub/hub-images/deploy_key.png index c4377bba9b..f1d8d92d22 100644 Binary files a/docs/sources/docker-hub/hub-images/deploy_key.png and b/docs/sources/docker-hub/hub-images/deploy_key.png differ diff --git a/docs/sources/docker-hub/hub-images/gh_docker-service.png b/docs/sources/docker-hub/hub-images/gh_docker-service.png index 0119b9e22a..7a84c81b7e 100644 Binary files a/docs/sources/docker-hub/hub-images/gh_docker-service.png and b/docs/sources/docker-hub/hub-images/gh_docker-service.png differ diff --git a/docs/sources/docker-hub/hub-images/gh_menu.png b/docs/sources/docker-hub/hub-images/gh_menu.png index d9c8d11996..84458a445f 100644 Binary files a/docs/sources/docker-hub/hub-images/gh_menu.png and b/docs/sources/docker-hub/hub-images/gh_menu.png differ diff --git a/docs/sources/docker-hub/hub-images/gh_service_hook.png b/docs/sources/docker-hub/hub-images/gh_service_hook.png index 9a00153bb1..c344c24afc 100644 Binary files a/docs/sources/docker-hub/hub-images/gh_service_hook.png and b/docs/sources/docker-hub/hub-images/gh_service_hook.png differ diff --git a/docs/sources/docker-hub/hub-images/gh_settings.png b/docs/sources/docker-hub/hub-images/gh_settings.png index efb1a3abf5..2af9cb5138 100644 Binary files a/docs/sources/docker-hub/hub-images/gh_settings.png and b/docs/sources/docker-hub/hub-images/gh_settings.png differ diff --git a/docs/sources/docker-hub/hub-images/github_deploy_key.png b/docs/sources/docker-hub/hub-images/github_deploy_key.png index bd69054b14..a0ec6a918f 100644 Binary files a/docs/sources/docker-hub/hub-images/github_deploy_key.png and b/docs/sources/docker-hub/hub-images/github_deploy_key.png differ diff --git a/docs/sources/docker-hub/hub.png b/docs/sources/docker-hub/hub.png index b260c5f62d..16840e0547 100644 Binary files a/docs/sources/docker-hub/hub.png and b/docs/sources/docker-hub/hub.png differ diff --git a/docs/sources/docker-hub/official_repos.md b/docs/sources/docker-hub/official_repos.md new file mode 100644 index 0000000000..5a948c6263 --- /dev/null +++ b/docs/sources/docker-hub/official_repos.md @@ -0,0 +1,189 @@ +page_title: Guidelines for Official Repositories on Docker Hub +page_description: Guidelines for Official Repositories on Docker Hub +page_keywords: Docker, docker, registry, accounts, plans, Dockerfile, Docker Hub, docs, official, image, documentation + +# Guidelines for Creating and Documenting Official Repositories + +## Introduction + +You’ve been given the job of creating an image for an Official Repository +hosted on [Docker Hub Registry](https://registry.hub.docker.com/). These are +our guidelines for getting that task done. Even if you’re not +planning to create an Official Repo, you can think of these guidelines as best +practices for image creation generally. + +This document consists of two major sections: + +* A list of expected files, resources and supporting items for your image, +along with best practices for creating those items +* Examples embodying those practices + +## Expected Files & Resources + +### A Git repository + +Your image needs to live in a Git repository, preferably on GitHub. (If you’d +like to use a different provider, please [contact us](mailto:feedback@docker.com) +directly.) Docker **strongly** recommends that this repo be publicly +accessible. + +If the repo is private or has otherwise limited access, you must provide a +means of at least “read-only” access for both general users and for the +docker-library maintainers, who need access for review and building purposes. + +### A Dockerfile + +Complete information on `Dockerfile`s can be found in the [Reference section](https://docs.docker.com/reference/builder/). +We also have a page discussing [best practices for writing `Dockerfile`s](/articles/dockerfile_best-practices). +Your `Dockerfile` should adhere to the following: + +* It must be written either by using `FROM scratch` or be based on another, +established Official Image. +* It must follow `Dockerfile` best practices. These are discussed on the +[best practices page](/articles/dockerfile_best-practices). In addition, +Docker engineer Michael Crosby has some good tips for `Dockerfiles` in +this [blog post](http://crosbymichael.com/dockerfile-best-practices-take-2.html). + +While [`ONBUILD` triggers](https://docs.docker.com/reference/builder/#onbuild) +are not required, if you choose to use them you should: + +* Build both `ONBUILD` and non-`ONBUILD` images, with the `ONBUILD` image +built `FROM` the non-`ONBUILD` image. +* The `ONBUILD` image should be specifically tagged, for example, `ruby: +latest`and `ruby:onbuild`, or `ruby:2` and `ruby:2-onbuild` + +### A short description + +Include a brief description of your image (in plaintext). Only one description +is required; you don’t need additional descriptions for each tag. The file +should also: + +* Be named `README-short.txt` +* Reside in the repo for the “latest” tag +* Not exceed 200 characters + +### A logo + +Include a logo of your company or the product (png format preferred). Only one +logo is required; you don’t need additional logo files for each tag. The logo +file should have the following characteristics: + +* Be named `logo.png` +* Should reside in the repo for the “latest” tag +* Should fit inside a 200px square, maximized in one dimension (preferably the +width) +* Square or wide (landscape) is preferred over tall (portrait), but exceptions +can be made based on the logo needed + +### A long description + +Include a comprehensive description of your image (in Markdown format, GitHub +flavor preferred). Only one description is required; you don’t need additional +descriptions for each tag. The file should also: + +* Be named `README.md` +* Reside in the repo for the “latest” tag +* Be no longer than absolutely necessary, while still addressing all the +content requirements + +In terms of content, the long description must include the following sections: + +* Overview & links +* How-to/usage +* Issues & contributions + +#### Overview & links + +This section should provide: + +* an overview of the software contained in the image, similar to the +introduction in a Wikipedia entry + +* a selection of links to outside resources that help to describe the software + +* a *mandatory* link to the `Dockerfile` + +#### How-to/usage + +A section that describes how to run and use the image, including common use +cases and example `Dockerfile`s (if applicable). Try to provide clear, step-by- +step instructions wherever possible. + +##### Issues & contributions + +In this section, point users to any resources that can help them contribute to +the project. Include contribution guidelines and any specific instructions +related to your development practices. Include a link to +[Docker’s resources for contributors](https://docs.docker.com/contributing/contributing/). +Be sure to include contact info, handles, etc. for official maintainers. + +Also include information letting users know where they can go for help and how +they can file issues with the repo. Point them to any specific IRC channels, +issue trackers, contacts, additional “how-to” information or other resources. + +### License + +Include a file, `LICENSE`, of any applicable license. Docker recommends using +the license of the software contained in the image, provided it allows Docker, +Inc. to legally build and distribute the image. Otherwise, Docker recommends +adopting the [Expat license](http://directory.fsf.org/wiki/License:Expat) +(a.k.a., the MIT or X11 license). + +## Examples + +Below are sample short and long description files for an imaginary image +containing Ruby on Rails. + +### Short description + +`README-short.txt` + +`Ruby on Rails is an open-source application framework written in Ruby. It emphasizes best practices such as convention over configuration, active record pattern, and the model-view-controller pattern.` + +### Long description + +`README.md` + +```markdown +# What is Ruby on Rails + +Ruby on Rails, often simply referred to as Rails, is an open source web application framework which runs via the Ruby programming language. It is a full-stack framework: it allows creating pages and applications that gather information from the web server, talk to or query the database, and render templates out of the box. As a result, Rails features a routing system that is independent of the web server. + +> [wikipedia.org/wiki/Ruby_on_Rails](https://en.wikipedia.org/wiki/Ruby_on_Rails) + +# How to use this image + +## Create a `Dockerfile` in your rails app project + + FROM rails:onbuild + +Put this file in the root of your app, next to the `Gemfile`. + +This image includes multiple `ONBUILD` triggers so that should be all that you need for most applications. The build will `ADD . /usr/src/app`, `RUN bundle install`, `EXPOSE 3000`, and set the default command to `rails server`. + +Then build and run the Docker image. + + docker build -t my-rails-app . + docker run --name some-rails-app -d my-rails-app + +Test it by visiting `http://container-ip:3000` in a browser. On the other hand, if you need access outside the host on port 8080: + + docker run --name some-rails-app -p 8080:3000 -d my-rails-app + +Then go to `http://localhost:8080` or `http://host-ip:8080` in a browser. +``` + +For more examples, take a look at these repos: + +* [Go](https://github.com/docker-library/golang) +* [PostgreSQL](https://github.com/docker-library/postgres) +* [Buildpack-deps](https://github.com/docker-library/buildpack-deps) +* ["Hello World" minimal container](https://github.com/docker-library/hello-world) +* [Node](https://github.com/docker-library/node) + +## Submit your repo + +Once you've checked off everything in these guidelines, and are confident your +image is ready for primetime, please contact us at +[partners@docker.com](mailto:partners@docker.com) to have your project +considered for the Official Repos program. diff --git a/docs/sources/docker-hub/orgs.png b/docs/sources/docker-hub/orgs.png index e6de3ddb42..f205d7f8fd 100644 Binary files a/docs/sources/docker-hub/orgs.png and b/docs/sources/docker-hub/orgs.png differ diff --git a/docs/sources/docker-hub/repos.md b/docs/sources/docker-hub/repos.md index 8d76d5a5df..42d97d8bf2 100644 --- a/docs/sources/docker-hub/repos.md +++ b/docs/sources/docker-hub/repos.md @@ -11,7 +11,7 @@ page_keywords: Docker, docker, registry, accounts, plans, Dockerfile, Docker Hub You can `search` for all the publicly available repositories and images using Docker. - $ docker search ubuntu + $ sudo docker search ubuntu This will show you a list of the currently available repositories on the Docker Hub which match the provided keyword. diff --git a/docs/sources/docker-hub/repos.png b/docs/sources/docker-hub/repos.png index c2d6bf4364..f25bb3a48d 100644 Binary files a/docs/sources/docker-hub/repos.png and b/docs/sources/docker-hub/repos.png differ diff --git a/docs/sources/examples/nodejs_web_app.md b/docs/sources/examples/nodejs_web_app.md index 5d69fd713b..d634251fb8 100644 --- a/docs/sources/examples/nodejs_web_app.md +++ b/docs/sources/examples/nodejs_web_app.md @@ -188,5 +188,4 @@ Now you can call your app using `curl` (install if needed via: We hope this tutorial helped you get up and running with Node.js and CentOS on Docker. You can get the full source code at -[https://github.com/gasi/docker-node-hello](https://github.com/gasi/docker-node-hello). - +[https://github.com/enokd/docker-node-hello/](https://github.com/enokd/docker-node-hello/). diff --git a/docs/sources/examples/postgresql_service.md b/docs/sources/examples/postgresql_service.md index ffd122ed58..9a4c1816d7 100644 --- a/docs/sources/examples/postgresql_service.md +++ b/docs/sources/examples/postgresql_service.md @@ -104,7 +104,7 @@ host-mapped port to test as well. You need to use `docker ps` to find out what local host port the container is mapped to first: - $ docker ps + $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5e24362f27f6 eg_postgresql:latest /usr/lib/postgresql/ About an hour ago Up About an hour 0.0.0.0:49153->5432/tcp pg_test $ psql -h localhost -p 49153 -d docker -U docker --password @@ -135,7 +135,7 @@ prompt, you can create a table and populate it. You can use the defined volumes to inspect the PostgreSQL log files and to backup your configuration and data: - $ docker run --rm --volumes-from pg_test -t -i busybox sh + $ sudo docker run --rm --volumes-from pg_test -t -i busybox sh / # ls bin etc lib linuxrc mnt proc run sys usr diff --git a/docs/sources/examples/running_riak_service.md b/docs/sources/examples/running_riak_service.md index c3d83bf663..0b53234046 100644 --- a/docs/sources/examples/running_riak_service.md +++ b/docs/sources/examples/running_riak_service.md @@ -101,7 +101,7 @@ Populate it with the following program definitions: Now you should be able to build a Docker image for Riak: - $ docker build -t "/riak" . + $ sudo docker build -t "/riak" . ## Next steps diff --git a/docs/sources/examples/running_ssh_service.Dockerfile b/docs/sources/examples/running_ssh_service.Dockerfile index 1b8ed02a8a..7aba7f6869 100644 --- a/docs/sources/examples/running_ssh_service.Dockerfile +++ b/docs/sources/examples/running_ssh_service.Dockerfile @@ -1,13 +1,20 @@ # sshd # -# VERSION 0.0.1 +# VERSION 0.0.2 -FROM ubuntu:12.04 -MAINTAINER Thatcher R. Peskens "thatcher@dotcloud.com" +FROM ubuntu:14.04 +MAINTAINER Sven Dowideit RUN apt-get update && apt-get install -y openssh-server RUN mkdir /var/run/sshd -RUN echo 'root:screencast' |chpasswd +RUN echo 'root:screencast' | chpasswd +RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config + +# SSH login fix. Otherwise user is kicked off after login +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd + +ENV NOTVISIBLE "in users profile" +RUN echo "export VISIBLE=now" >> /etc/profile EXPOSE 22 -CMD ["/usr/sbin/sshd", "-D"] +CMD ["/usr/sbin/sshd", "-D"] diff --git a/docs/sources/examples/running_ssh_service.md b/docs/sources/examples/running_ssh_service.md index 7140678e3b..9f87fb726d 100644 --- a/docs/sources/examples/running_ssh_service.md +++ b/docs/sources/examples/running_ssh_service.md @@ -4,28 +4,39 @@ page_keywords: docker, example, package installation, networking # Dockerizing an SSH Daemon Service +## Build an `eg_sshd` image + The following `Dockerfile` sets up an SSHd service in a container that you can use to connect to and inspect other container's volumes, or to get quick access to a test container. # sshd # - # VERSION 0.0.1 + # VERSION 0.0.2 - FROM ubuntu:12.04 - MAINTAINER Thatcher R. Peskens "thatcher@dotcloud.com" + FROM ubuntu:14.04 + MAINTAINER Sven Dowideit RUN apt-get update && apt-get install -y openssh-server RUN mkdir /var/run/sshd - RUN echo 'root:screencast' |chpasswd + RUN echo 'root:screencast' | chpasswd + RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config + + # SSH login fix. Otherwise user is kicked off after login + RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd + + ENV NOTVISIBLE "in users profile" + RUN echo "export VISIBLE=now" >> /etc/profile EXPOSE 22 - CMD ["/usr/sbin/sshd", "-D"] + CMD ["/usr/sbin/sshd", "-D"] Build the image using: $ sudo docker build -t eg_sshd . +## Run a `test_sshd` container + Then run it. You can then use `docker port` to find out what host port the container's port 22 is mapped to: @@ -33,13 +44,30 @@ the container's port 22 is mapped to: $ sudo docker port test_sshd 22 0.0.0.0:49154 -And now you can ssh to port `49154` on the Docker daemon's host IP -address (`ip address` or `ifconfig` can tell you that): +And now you can ssh as `root` on the container's IP address (you can find it +with `docker inspect`) or on port `49154` of the Docker daemon's host IP address +(`ip address` or `ifconfig` can tell you that): $ ssh root@192.168.1.2 -p 49154 # The password is ``screencast``. $$ +## Environment variables + +Using the `sshd` daemon to spawn shells makes it complicated to pass environment +variables to the user's shell via the simple Docker mechanisms, as `sshd` scrubs +the environment before it starts the shell. + +If you're setting values in the Dockerfile using `ENV`, you'll need to push them +to a shell initialisation file like the `/etc/profile` example in the Dockerfile +above. + +If you need to pass`docker run -e ENV=value` values, you will need to write a +short script to do the same before you start `sshd -D` - and then replace the +`CMD` with that script. + +## Clean up + Finally, clean up after your test by stopping and removing the container, and then removing the image. diff --git a/docs/sources/http-routingtable.md b/docs/sources/http-routingtable.md index 9fd78d03b5..4de7bcd3fa 100644 --- a/docs/sources/http-routingtable.md +++ b/docs/sources/http-routingtable.md @@ -44,6 +44,7 @@ [`POST /containers/(id)/wait`](../reference/api/docker_remote_api_v1.9/#post--containers-(id)-wait) ** [`POST /containers/create`](../reference/api/docker_remote_api_v1.9/#post--containers-create) ** [`GET /containers/json`](../reference/api/docker_remote_api_v1.9/#get--containers-json) ** + [`GET /containers/resize`](../reference/api/docker_remote_api_v1.9/#get--containers-resize) **   **/events** [`GET /events`](../reference/api/docker_remote_api_v1.9/#get--events) ** diff --git a/docs/sources/index.md b/docs/sources/index.md index 5267557f38..7e60b0dc6d 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -83,51 +83,45 @@ Docker on a variety of platforms. ### Docker User Guide -To learn about Docker in more detail and to answer questions about usage and implementation, check out the [Docker User Guide](/userguide/). +To learn about Docker in more detail and to answer questions about usage and +implementation, check out the [Docker User Guide](/userguide/). ## Release Notes -Version 1.1.0 +**Version 1.3.0** -### New Features +This version fixes a number of bugs and issues and adds new functions and other +improvements. These include: -*`.dockerignore` support* +*New command: `docker exec`* -You can now add a `.dockerignore` file next to your `Dockerfile` and Docker will ignore files and directories specified in that file when sending the build context to the daemon. -Example: https://github.com/docker/docker/blob/master/.dockerignore +The new `docker exec` command lets you run a process in an existing, active +container. The command has APIs for both the daemon and the client. With +`docker exec`, you'll be able to do things like add or remove devices from running containers, debug running containers, and run commands that are not +part of the container's static specification. -*Pause containers during commit* +*New command: `docker create`* -Doing a commit on a running container was not recommended because you could end up with files in an inconsistent state (for example, if they were being written during the commit). Containers are now paused when a commit is made to them. -You can disable this feature by doing a `docker commit --pause=false ` +Traditionally, the `docker run` command has been used to both create a +container and spawn a process to run it. The new `docker create` command breaks +this apart, letting you set up a container without actually starting it. This +provides more control over management of the container lifecycle, giving you the +ability to configure things like volumes or port mappings before the container +is started. For example, in a rapid-response scaling situation, you could use +`create` to prepare and stage ten containers in anticipation of heavy loads. -*Tailing logs* +*New provenance features* -You can now tail the logs of a container. For example, you can get the last ten lines of a log by using `docker logs --tail 10 `. You can also follow the logs of a container without having to read the whole log file with `docker logs --tail 0 -f `. - -*Allow a tar file as context for docker build* - -You can now pass a tar archive to `docker build` as context. This can be used to automate docker builds, for example: `cat context.tar | docker build -` or `docker run builder_image | docker build -` - -*Bind mounting your whole filesystem in a container* - -`/` is now allowed as source of `--volumes`. This means you can bind-mount your whole system in a container if you need to. For example: `docker run -v /:/my_host ubuntu:ro ls /my_host`. However, it is now forbidden to mount to /. +Official images are now signed by Docker, Inc. to improve your confidence and +security. Look for the blue ribbons on the [Docker Hub](https://hub.docker.com/). +The Docker Engine has been updated to automatically verify that a given Official +Repo has a current, valid signature. If no valid signature is detected, Docker +Engine will use a prior image. -### Other Improvements & Changes - -* Port allocation has been improved. In the previous release, Docker could prevent you from starting a container with previously allocated ports which seemed to be in use when in fact they were not. This has been fixed. - -* A bug in `docker save` was introduced in the last release. The `docker save` command could produce images with invalid metadata. The command now produces images with correct metadata. - -* Running `docker inspect` in a container now returns which containers it is linked to. - -* Parsing of the `docker commit` flag has improved validation, to better prevent you from committing an image with a name such as `-m`. Image names with dashes in them potentially conflict with command line flags. - -* The API now has Improved status codes for `start` and `stop`. Trying to start a running container will now return a 304 error. - -* Performance has been improved overall. Starting the daemon is faster than in previous releases. The daemon’s performance has also been improved when it is working with large numbers of images and containers. - -* Fixed an issue with white-spaces and multi-lines in Dockerfiles. +*Other improvements & changes* +We've added a new security options flag that lets you set SELinux and AppArmor +labels and profiles. This means you'll longer have to use `docker run +--privileged on kernels that support SE Linux or AppArmor. diff --git a/docs/sources/installation/amazon.md b/docs/sources/installation/amazon.md index 86443147e7..3715d5c44f 100644 --- a/docs/sources/installation/amazon.md +++ b/docs/sources/installation/amazon.md @@ -32,7 +32,7 @@ course.** - When you're on the "Configure Instance Details" step, expand the "Advanced Details" section. - Under "User data", select "As text". - - Enter `#include https://get.docker.io` into + - Enter `#include https://get.docker.com` into the instance *User Data*. [CloudInit](https://help.ubuntu.com/community/CloudInit) is part of the Ubuntu image you chose; it will bootstrap Docker by @@ -46,7 +46,7 @@ Security Group to allow SSH.** By default all incoming ports to your new instance will be blocked by the AWS Security Group, so you might just get timeouts when you try to connect. -Installing with `get.docker.io` (as above) will +Installing with `get.docker.com` (as above) will create a service named `lxc-docker`. It will also set up a [*docker group*](../binaries/#dockergroup) and you may want to add the *ubuntu* user to it so that you don't have to use diff --git a/docs/sources/installation/binaries.md b/docs/sources/installation/binaries.md index 8e35d98030..da2b195c0f 100644 --- a/docs/sources/installation/binaries.md +++ b/docs/sources/installation/binaries.md @@ -32,22 +32,28 @@ runtime: Docker in daemon mode has specific kernel requirements. For details, check your distribution in [*Installation*](../#installation-list). -In general, a 3.8 Linux kernel (or higher) is preferred, as some of the -prior versions have known issues that are triggered by Docker. +In general, a 3.8 Linux kernel is the minimum requirement for Docker, as +some of the prior versions have known issues that are triggered by Docker. +Linux kernel versions older than 3.8 are known to cause kernel panics and +to break Docker. + +The latest minor version (3.x.y) of the 3.10 (or a newer maintained version) +Linux kernel is recommended. Keeping the kernel up to date with the latest +minor version will ensure critical kernel bugs get fixed. Note that Docker also has a client mode, which can run on virtually any Linux kernel (it even builds on OS X!). ## Get the docker binary: - $ wget https://get.docker.io/builds/Linux/x86_64/docker-latest -O docker + $ wget https://get.docker.com/builds/Linux/x86_64/docker-latest -O docker $ chmod +x docker > **Note**: > If you have trouble downloading the binary, you can also get the smaller > compressed release file: -> [https://get.docker.io/builds/Linux/x86_64/docker-latest.tgz]( -> https://get.docker.io/builds/Linux/x86_64/docker-latest.tgz) +> [https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz]( +> https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz) ## Run the docker daemon diff --git a/docs/sources/installation/centos.md b/docs/sources/installation/centos.md index b919ca5806..2f7d57d604 100644 --- a/docs/sources/installation/centos.md +++ b/docs/sources/installation/centos.md @@ -4,10 +4,12 @@ page_keywords: Docker, Docker documentation, requirements, linux, centos, epel, # CentOS -While the Docker package is provided by default as part of CentOS-7, -it is provided by a community repository for CentOS-6. Please note that -this changes the installation instructions slightly between versions. - +While the Docker package is provided by default as part of CentOS-7, +it is provided by the EPEL repository for CentOS-6. Please note that +this changes the installation instructions slightly between versions. If you +need the latest version, you can always use the latest binary which works on +kernel 3.8 and above. + These instructions work for CentOS 6 and later. They will likely work for other binary compatible EL6 distributions such as Scientific Linux, but they haven't been tested. @@ -25,6 +27,30 @@ simply run the following command. $ sudo yum install docker +### Manual installation of latest version + +While using a package is the recommended way of installing Docker, +the above package might not be the latest version. If you need the latest +version, [you can install the binary directly]( +https://docs.docker.com/installation/binaries/). + +When installing the binary without a package, you may want +to integrate Docker with systemd. For this, simply install the two unit files +(service and socket) from [the github +repository](https://github.com/docker/docker/tree/master/contrib/init/systemd) +to `/etc/systemd/system`. + +### FirewallD + +CentOS-7 introduced firewalld, which is a wrapper around iptables and can +conflict with Docker. + +When firewalld is started or restarted it will remove the `DOCKER` chain +from iptables, preventing Docker from working properly. + +When using systemd, firewalld is started before Docker, but if you +start or restart firewalld after Docker, you will have to restart the Docker daemon. + ## Installing Docker - CentOS-6 Please note that this for CentOS-6, this package is part of [Extra Packages for Enterprise Linux (EPEL)](https://fedoraproject.org/wiki/EPEL), a community effort diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 0da2f2f5d0..1db1609699 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -7,6 +7,7 @@ page_keywords: Docker, Docker documentation, installation, debian Docker is supported on the following versions of Debian: - [*Debian 8.0 Jessie (64-bit)*](#debian-jessie-8-64-bit) + - [*Debian 7.5 Wheezy (64-bit)*](#debian-wheezy-7-64-bit) ## Debian Jessie 8.0 (64-bit) @@ -34,7 +35,35 @@ Which should download the `ubuntu` image, and then start `bash` in a container. > If you want to enable memory and swap accounting see > [this](/installation/ubuntulinux/#memory-and-swap-accounting). -### Giving non-root access +## Debian Wheezy/Stable 7.x (64-bit) + +Docker requires Kernel 3.8+, while Wheezy ships with Kernel 3.2 (for more details +on why 3.8 is required, see discussion on +[bug #407](https://github.com/docker/docker/issues/407%20kernel%20versions)). + +Fortunately, wheezy-backports currently has [Kernel 3.14 +](https://packages.debian.org/search?suite=wheezy-backports§ion=all&arch=any&searchon=names&keywords=linux-image-amd64), +which is officially supported by Docker. + +### Installation + +1. Install Kernel 3.14 from wheezy-backports + + Add the following line to your `/etc/apt/sources.list` + + `deb http://http.debian.net/debian wheezy-backports main` + + then install the `linux-image-amd64` package (note the use of + `-t wheezy-backports`) + + $ sudo apt-get update + $ sudo apt-get install -t wheezy-backports linux-image-amd64 + +2. Install Docker using the get.docker.com script: + + `curl -sSL https://get.docker.com/ | sh` + +## Giving non-root access The `docker` daemon always runs as the `root` user and the `docker` daemon binds to a Unix socket instead of a TCP port. By default that @@ -68,7 +97,7 @@ use the `-G` flag to specify an alternative group. # Restart the Docker daemon. $ sudo service docker restart + ## What next? Continue with the [User Guide](/userguide/). - diff --git a/docs/sources/installation/fedora.md b/docs/sources/installation/fedora.md index 757b3e9c44..9101ef1356 100644 --- a/docs/sources/installation/fedora.md +++ b/docs/sources/installation/fedora.md @@ -48,6 +48,10 @@ Now let's verify that Docker is working. $ sudo docker run -i -t fedora /bin/bash +> Note: If you get a `Cannot start container` error mentioning SELinux +> or permission denied, you may need to update the SELinux policies. +> This can be done using `sudo yum upgrade selinux-policy` and then rebooting. + ## Granting rights to users to use Docker Fedora 19 and 20 shipped with Docker 0.11. The package has already been updated diff --git a/docs/sources/installation/gentoolinux.md b/docs/sources/installation/gentoolinux.md index ac92ad39c8..39333e63e6 100644 --- a/docs/sources/installation/gentoolinux.md +++ b/docs/sources/installation/gentoolinux.md @@ -4,53 +4,77 @@ page_keywords: gentoo linux, virtualization, docker, documentation, installation # Gentoo -Installing Docker on Gentoo Linux can be accomplished using one of two -methods. The first and best way if you're looking for a stable -experience is to use the official app-emulation/docker package directly -in the portage tree. +Installing Docker on Gentoo Linux can be accomplished using one of two ways: the **official** way and the `docker-overlay` way. -If you're looking for a `-bin` ebuild, a live ebuild, or bleeding edge -ebuild changes/fixes, the second installation method is to use the -overlay provided at -[https://github.com/tianon/docker-overlay](https://github.com/tianon/docker-overlay) +Official project page of [Gentoo Docker](https://wiki.gentoo.org/wiki/Project:Docker) team. + +## Official way +The first and recommended way if you are looking for a stable +experience is to use the official `app-emulation/docker` package directly +from the tree. + +If any issues arise from this ebuild including, missing kernel +configuration flags or dependencies, open a bug +on the Gentoo [Bugzilla](https://bugs.gentoo.org) assigned to `docker AT gentoo DOT org` +or join and ask in the official +[IRC](http://webchat.freenode.net?channels=%23gentoo-containers&uio=d4) channel on the Freenode network. + +## docker-overlay way + +If you're looking for a `-bin` ebuild, a live ebuild, or a bleeding edge +ebuild, use the provided overlay, [docker-overlay](https://github.com/tianon/docker-overlay) which can be added using `app-portage/layman`. The most accurate and up-to-date documentation for properly installing and using the overlay -can be found in [the overlay -README](https://github.com/tianon/docker-overlay/blob/master/README.md#using-this-overlay). +can be found in the [overlay](https://github.com/tianon/docker-overlay/blob/master/README.md#using-this-overlay). -Note that sometimes there is a disparity between the latest version and -what's in the overlay, and between the latest version in the overlay and -what's in the portage tree. Please be patient, and the latest version -should propagate shortly. +If any issues arise from this ebuild or the resulting binary, including +and especially missing kernel configuration flags or dependencies, +open an [issue](https://github.com/tianon/docker-overlay/issues) on +the `docker-overlay` repository or ping `tianon` directly in the `#docker` +IRC channel on the Freenode network. ## Installation +### Available USE flags + +| USE Flag | Default | Description | +| ------------- |:-------:|:------------| +| aufs | |Enables dependencies for the "aufs" graph driver, including necessary kernel flags.| +| btrfs | |Enables dependencies for the "btrfs" graph driver, including necessary kernel flags.| +| contrib | Yes |Install additional contributed scripts and components.| +| device-mapper | Yes |Enables dependencies for the "devicemapper" graph driver, including necessary kernel flags.| +| doc | |Add extra documentation (API, Javadoc, etc). It is recommended to enable per package instead of globally.| +| lxc | |Enables dependencies for the "lxc" execution driver.| +| vim-syntax | |Pulls in related vim syntax scripts.| +| zsh-completion| |Enable zsh completion support.| + +USE flags are described in detail on [tianon's +blog](https://tianon.github.io/post/2014/05/17/docker-on-gentoo.html). + The package should properly pull in all the necessary dependencies and -prompt for all necessary kernel options. The ebuilds for 0.7+ include -use flags to pull in the proper dependencies of the major storage -drivers, with the "device-mapper" use flag being enabled by default, -since that is the simplest installation path. +prompt for all necessary kernel options. $ sudo emerge -av app-emulation/docker -If any issues arise from this ebuild or the resulting binary, including -and especially missing kernel configuration flags and/or dependencies, -[open an issue on the docker-overlay repository]( -https://github.com/tianon/docker-overlay/issues) or ping -tianon directly in the #docker IRC channel on the freenode network. - -Other use flags are described in detail on [tianon's -blog](https://tianon.github.io/post/2014/05/17/docker-on-gentoo.html). +>Note: Sometimes there is a disparity between the latest versions +>in the official **Gentoo tree** and the **docker-overlay**. +>Please be patient, and the latest version should propagate shortly. ## Starting Docker Ensure that you are running a kernel that includes all the necessary -modules and/or configuration for LXC (and optionally for device-mapper -and/or AUFS, depending on the storage driver you`ve decided to use). +modules and configuration (and optionally for device-mapper +and AUFS or Btrfs, depending on the storage driver you've decided to use). +To use Docker, the `docker` daemon must be running as **root**. +To use Docker as a **non-root** user, add yourself to the **docker** +group by running the following command: + + $ sudo usermod -a -G docker user + ### OpenRC -To start the docker daemon: +To start the `docker` daemon: $ sudo /etc/init.d/docker start @@ -60,10 +84,10 @@ To start on system boot: ### systemd -To start the docker daemon: +To start the `docker` daemon: - $ sudo systemctl start docker.service + $ sudo systemctl start docker To start on system boot: - $ sudo systemctl enable docker.service + $ sudo systemctl enable docker diff --git a/docs/sources/installation/google.md b/docs/sources/installation/google.md index 23a9bfbfb8..cbd1f8b63a 100644 --- a/docs/sources/installation/google.md +++ b/docs/sources/installation/google.md @@ -20,7 +20,7 @@ page_keywords: Docker, Docker documentation, installation, google, Google Comput (select a zone close to you and the desired instance size) $ gcloud compute instances create docker-playground \ - --image container-vm-v20140730 \ + --image container-vm-v20140925 \ --image-project google-containers \ --zone us-central1-a \ --machine-type f1-micro @@ -28,7 +28,7 @@ page_keywords: Docker, Docker documentation, installation, google, Google Comput 4. Connect to the instance using SSH: $ gcloud compute ssh --zone us-central1-a docker-playground - $$ docker-playground:~$ sudo docker run hello-world + docker-playground:~$ sudo docker run hello-world Hello from Docker. This message shows that your installation appears to be working correctly. ... diff --git a/docs/sources/installation/images/osx-installer.png b/docs/sources/installation/images/osx-installer.png index dbb6bcd2d9..15eb40839f 100644 Binary files a/docs/sources/installation/images/osx-installer.png and b/docs/sources/installation/images/osx-installer.png differ diff --git a/docs/sources/installation/images/windows-boot2docker-start.png b/docs/sources/installation/images/windows-boot2docker-start.png index 189289638d..9d598d56bf 100644 Binary files a/docs/sources/installation/images/windows-boot2docker-start.png and b/docs/sources/installation/images/windows-boot2docker-start.png differ diff --git a/docs/sources/installation/images/windows-installer.png b/docs/sources/installation/images/windows-installer.png index 6305dcc6d9..ea1d3d2326 100644 Binary files a/docs/sources/installation/images/windows-installer.png and b/docs/sources/installation/images/windows-installer.png differ diff --git a/docs/sources/installation/mac.md b/docs/sources/installation/mac.md index da0e172892..0707c56b7b 100644 --- a/docs/sources/installation/mac.md +++ b/docs/sources/installation/mac.md @@ -34,7 +34,7 @@ VirtualBox VM, Docker itself, and the Boot2Docker management tool. $ boot2docker init $ boot2docker start - $ export DOCKER_HOST=tcp://$(boot2docker ip 2>/dev/null):2375 + $ $(boot2docker shellinit) A terminal window will open and you'll see the virtual machine starting up. Once you have an initialized virtual machine, you can control it with `boot2docker stop` @@ -46,7 +46,7 @@ and `boot2docker start`. > `To connect the Docker client to the Docker daemon, please set: export DOCKER_HOST=tcp://192.168.59.103:2375` > -you can safely set the evironment variable as instructed. +you can safely set the environment variable as instructed. View the [Boot2Docker ReadMe](https://github.com/boot2docker/boot2docker/blob/master/README.md) @@ -67,7 +67,8 @@ terminal window and run: $ boot2docker download $ boot2docker start -This will download an .iso containing a fresh VM and start it up. +This will download an .iso image containing a fresh VM and start it up. Your upgrade is +complete. You can test it by following the directions below. ## Running Docker diff --git a/docs/sources/installation/oracle.md b/docs/sources/installation/oracle.md new file mode 100644 index 0000000000..05bb3d9808 --- /dev/null +++ b/docs/sources/installation/oracle.md @@ -0,0 +1,120 @@ +page_title: Installation on Oracle Linux +page_description: Installation instructions for Docker on Oracle Linux. +page_keywords: Docker, Docker documentation, requirements, linux, rhel, centos, oracle, ol + +# Oracle Linux 6 and 7 + +You do not require an Oracle Linux Support subscription to install Docker on +Oracle Linux. + +*For Oracle Linux customers with an active support subscription:* +Docker is available in either the `ol6_x86_64_addons` or `ol7_x86_64_addons` +channel for Oracle Linux 6 and Oracle Linux 7 on the [Unbreakable Linux Network +(ULN)](https://linux.oracle.com). + +*For Oracle Linux users without an active support subscription:* +Docker is available in the appropriate `ol6_addons` or `ol7_addons` repository +on [Oracle Public Yum](http://public-yum.oracle.com). + +Docker requires the use of the Unbreakable Enterprise Kernel Release 3 (3.8.13) +or higher on Oracle Linux. This kernel supports the Docker btrfs storage engine +on both Oracle Linux 6 and 7. + +Due to current Docker limitations, Docker is only able to run only on the x86_64 +architecture. + +## To enable the *addons* channel via the Unbreakable Linux Network: + +1. Enable either the *ol6\_x86\_64\_addons* or *ol7\_x86\_64\_addons* channel +via the ULN web interface. +Consult the [Unbreakable Linux Network User's +Guide](http://docs.oracle.com/cd/E52668_01/E39381/html/index.html) for +documentation on subscribing to channels. + +## To enable the *addons* repository via Oracle Public Yum: + +The latest release of Oracle Linux 6 and 7 are automatically configured to use +the Oracle Public Yum repositories during installation. However, the *addons* +repository is not enabled by default. + +To enable the *addons* repository: + +1. Edit either `/etc/yum.repos.d/public-yum-ol6.repo` or +`/etc/yum.repos.d/public-yum-ol7.repo` +and set `enabled=1` in the `[ol6_addons]` or the `[ol7_addons]` stanza. + +## To install Docker: + +1. Ensure the appropriate *addons* channel or repository has been enabled. + +2. Use yum to install the Docker package: + + $ sudo yum install docker + +## To start Docker: + +1. Now that it's installed, start the Docker daemon: + + 1. On Oracle Linux 6: + + $ sudo service docker start + + 2. On Oracle Linux 7: + + $ sudo systemctl start docker.service + +2. If you want the Docker daemon to start automatically at boot: + + 1. On Oracle Linux 6: + + $ sudo chkconfig docker on + + 2. On Oracle Linux 7: + + $ sudo systemctl enable docker.service + +**Done!** + +## Using the btrfs storage engine + +Docker on Oracle Linux 6 and 7 supports the use of the btrfs storage engine. +Before enabling btrfs support, ensure that `/var/lib/docker` is stored on a +btrfs-based filesystem. Review [Chapter +5](http://docs.oracle.com/cd/E37670_01/E37355/html/ol_btrfs.html) of the [Oracle +Linux Administrator's Solution +Guide](http://docs.oracle.com/cd/E37670_01/E37355/html/index.html) for details +on how to create and mount btrfs filesystems. + +To enable btrfs support on Oracle Linux: + +1. Ensure that `/var/lib/docker` is on a btrfs filesystem. +1. Edit `/etc/sysconfig/docker` and add `-s btrfs` to the `OTHER_ARGS` field. +2. Restart the Docker daemon: + +You can now continue with the [Docker User Guide](/userguide/). + +## Known issues + +### Docker unmounts btrfs filesystem on shutdown +If you're running Docker using the btrfs storage engine and you stop the Docker +service, it will unmount the btrfs filesystem during the shutdown process. You +should ensure the filesystem is mounted properly prior to restarting the Docker +service. + +On Oracle Linux 7, you can use a `systemd.mount` definition and modify the +Docker `systemd.service` to depend on the btrfs mount defined in systemd. + +### SElinux Support on Oracle Linux 7 +SElinux must be set to `Permissive` or `Disabled` in `/etc/sysconfig/selinux` to +use the btrfs storage engine on Oracle Linux 7. + +## Further issues? + +If you have a current Basic or Premier Support Subscription for Oracle Linux, +you can report any issues you have with the installation of Docker via a Service +Request at [My Oracle Support](http://support.oracle.com). + +If you do not have an Oracle Linux Support Subscription, you can use the [Oracle +Linux +Forum](https://community.oracle.com/community/server_%26_storage_systems/linux/ +oracle_linux) for community-based support. diff --git a/docs/sources/installation/rhel.md b/docs/sources/installation/rhel.md index a8b785acca..74a293b513 100644 --- a/docs/sources/installation/rhel.md +++ b/docs/sources/installation/rhel.md @@ -75,6 +75,10 @@ Now let's verify that Docker is working. $ sudo docker run -i -t fedora /bin/bash +> Note: If you get a `Cannot start container` error mentioning SELinux +> or permission denied, you may need to update the SELinux policies. +> This can be done using `sudo yum upgrade selinux-policy` and then rebooting. + **Done!** Continue with the [User Guide](/userguide/). diff --git a/docs/sources/installation/ubuntulinux.md b/docs/sources/installation/ubuntulinux.md index 673ea18b0c..efeeeea2e1 100644 --- a/docs/sources/installation/ubuntulinux.md +++ b/docs/sources/installation/ubuntulinux.md @@ -31,6 +31,7 @@ To install the latest Ubuntu package (may not be the latest Docker release): $ sudo apt-get install docker.io $ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker $ sudo sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker.io + $ source /etc/bash_completion.d/docker.io If you'd like to try the latest version of Docker: @@ -54,7 +55,7 @@ the `lxc-docker` package. *You may receive a warning that the package isn't trusted. Answer yes to continue installation.* - $ sudo sh -c "echo deb https://get.docker.io/ubuntu docker main\ + $ sudo sh -c "echo deb https://get.docker.com/ubuntu docker main\ > /etc/apt/sources.list.d/docker.list" $ sudo apt-get update $ sudo apt-get install lxc-docker @@ -63,7 +64,7 @@ continue installation.* > > There is also a simple `curl` script available to help with this process. > -> $ curl -sSL https://get.docker.io/ubuntu/ | sudo sh +> $ curl -sSL https://get.docker.com/ubuntu/ | sudo sh To verify that everything has worked as expected: @@ -87,9 +88,18 @@ VirtualBox guest additions. If you didn't install the headers for your "precise" kernel, then you can skip these headers for the "raring" kernel. But it is safer to include them if you're not sure. +Please read the installation instructions for backported kernels at +Ubuntu.org to understand why you also need to install the Xorg packages +when running Docker on a machine with a graphical environment like Unity. +[LTS Enablement Stack](https://wiki.ubuntu.com/Kernel/LTSEnablementStack) refer to note 5 under +each version. + # install the backported kernel $ sudo apt-get update $ sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring + + # install the backported kernel and xorg if using Unity/Xorg + $ sudo apt-get install --install-recommends linux-generic-lts-raring xserver-xorg-lts-raring libgl1-mesa-glx-lts-raring # reboot $ sudo reboot @@ -125,7 +135,7 @@ the `lxc-docker` package. *You may receive a warning that the package isn't trusted. Answer yes to continue installation.* - $ sudo sh -c "echo deb https://get.docker.io/ubuntu docker main\ + $ sudo sh -c "echo deb https://get.docker.com/ubuntu docker main\ > /etc/apt/sources.list.d/docker.list" $ sudo apt-get update $ sudo apt-get install lxc-docker @@ -134,7 +144,7 @@ continue installation.* > > There is also a simple `curl` script available to help with this process. > -> $ curl -sSL https://get.docker.io/ubuntu/ | sudo sh +> $ curl -sSL https://get.docker.com/ubuntu/ | sudo sh Now verify that the installation has worked by downloading the `ubuntu` image and launching a container. @@ -178,7 +188,7 @@ First add the Docker repository key to your local keychain. Add the Docker repository to your apt sources list, update and install the `lxc-docker` package. - $ sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ + $ sudo sh -c "echo deb http://get.docker.com/ubuntu docker main\ > /etc/apt/sources.list.d/docker.list" $ sudo apt-get update $ sudo apt-get install lxc-docker @@ -347,7 +357,7 @@ NetworkManager and Docker need to be restarted afterwards: ## Mirrors -You should `ping get.docker.io` and compare the +You should `ping get.docker.com` and compare the latency to the following mirrors, and pick whichever one is best for you. @@ -356,7 +366,7 @@ you. [Yandex](http://yandex.ru/) in Russia is mirroring the Docker Debian packages, updating every 6 hours. Substitute `http://mirror.yandex.ru/mirrors/docker/` for -`http://get.docker.io/ubuntu` in the instructions above. +`http://get.docker.com/ubuntu` in the instructions above. For example: $ sudo sh -c "echo deb http://mirror.yandex.ru/mirrors/docker/ docker main\ diff --git a/docs/sources/introduction/understanding-docker.md b/docs/sources/introduction/understanding-docker.md index 9448f68d8b..c561459e67 100644 --- a/docs/sources/introduction/understanding-docker.md +++ b/docs/sources/introduction/understanding-docker.md @@ -198,7 +198,7 @@ then run. Either by using the `docker` binary or via the API, the Docker client tells the Docker daemon to run a container. - $ docker run -i -t ubuntu /bin/bash + $ sudo docker run -i -t ubuntu /bin/bash Let's break down this command. The Docker client is launched using the `docker` binary with the `run` option telling it to launch a new container. The bare diff --git a/docs/sources/jsearch.md b/docs/sources/jsearch.md index 0e2def2f70..4a00e74dc7 100644 --- a/docs/sources/jsearch.md +++ b/docs/sources/jsearch.md @@ -1,3 +1,8 @@ +page_title: Search the Docker documentation +page_keywords: Docker, search documentation +no_toc: true +no_version_dropdown: true + # Search
+Sorry, page not found.
diff --git a/docs/sources/reference/api/_static/io_oauth_authorization_page.png b/docs/sources/reference/api/_static/io_oauth_authorization_page.png index 798044ed04..455d631e17 100644 Binary files a/docs/sources/reference/api/_static/io_oauth_authorization_page.png and b/docs/sources/reference/api/_static/io_oauth_authorization_page.png differ diff --git a/docs/sources/reference/api/docker-io_api.md b/docs/sources/reference/api/docker-io_api.md index e34e43f3bf..c21781a42a 100644 --- a/docs/sources/reference/api/docker-io_api.md +++ b/docs/sources/reference/api/docker-io_api.md @@ -4,23 +4,21 @@ page_keywords: API, Docker, index, REST, documentation, Docker Hub, registry # Docker Hub API -## Introduction - - This is the REST API for [Docker Hub](https://hub.docker.com). - Authorization is done with basic auth over SSL - Not all commands require authentication, only those noted as such. -## Repository +# Repositories -### Repositories +## User Repository -#### User Repo +### Create a user repository `PUT /v1/repositories/(namespace)/(repo_name)/` Create a user repository with the given `namespace` and `repo_name`. - **Example Request**: +**Example Request**: PUT /v1/repositories/foo/bar/ HTTP/1.1 Host: index.docker.io @@ -31,12 +29,12 @@ Create a user repository with the given `namespace` and `repo_name`. [{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"}] - Parameters: +Parameters: - - **namespace** – the namespace for the repo - - **repo_name** – the name for the repo +- **namespace** – the namespace for the repo +- **repo_name** – the name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -47,18 +45,20 @@ Create a user repository with the given `namespace` and `repo_name`. "" - Status Codes: +Status Codes: - - **200** – Created - - **400** – Errors (invalid json, missing or invalid fields, etc) - - **401** – Unauthorized - - **403** – Account is not Active +- **200** – Created +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Account is not Active + +### Delete a user repository `DELETE /v1/repositories/(namespace)/(repo_name)/` Delete a user repository with the given `namespace` and `repo_name`. - **Example Request**: +**Example Request**: DELETE /v1/repositories/foo/bar/ HTTP/1.1 Host: index.docker.io @@ -69,12 +69,12 @@ Delete a user repository with the given `namespace` and `repo_name`. "" - Parameters: +Parameters: - - **namespace** – the namespace for the repo - - **repo_name** – the name for the repo +- **namespace** – the namespace for the repo +- **repo_name** – the name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 202 Vary: Accept @@ -85,25 +85,27 @@ Delete a user repository with the given `namespace` and `repo_name`. "" - Status Codes: +Status Codes: - - **200** – Deleted - - **202** – Accepted - - **400** – Errors (invalid json, missing or invalid fields, etc) - - **401** – Unauthorized - - **403** – Account is not Active +- **200** – Deleted +- **202** – Accepted +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Account is not Active -#### Library Repo +## Library Repository + +### Create a library repository `PUT /v1/repositories/(repo_name)/` Create a library repository with the given `repo_name`. This is a restricted feature only available to docker admins. - When namespace is missing, it is assumed to be `library` +> When namespace is missing, it is assumed to be `library` - **Example Request**: +**Example Request**: PUT /v1/repositories/foobar/ HTTP/1.1 Host: index.docker.io @@ -114,11 +116,11 @@ This is a restricted feature only available to docker admins. [{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"}] - Parameters: +Parameters: - - **repo_name** – the library name for the repo +- **repo_name** – the library name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -129,22 +131,24 @@ This is a restricted feature only available to docker admins. "" - Status Codes: +Status Codes: - - **200** – Created - - **400** – Errors (invalid json, missing or invalid fields, etc) - - **401** – Unauthorized - - **403** – Account is not Active +- **200** – Created +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Account is not Active + +### Delete a library repository `DELETE /v1/repositories/(repo_name)/` -Delete a library repository with the given `repo_name`. +Delete a library repository with the given `repo_name`. This is a restricted feature only available to docker admins. - When namespace is missing, it is assumed to be `library` +> When namespace is missing, it is assumed to be `library` - **Example Request**: +**Example Request**: DELETE /v1/repositories/foobar/ HTTP/1.1 Host: index.docker.io @@ -155,11 +159,11 @@ This is a restricted feature only available to docker admins. "" - Parameters: +Parameters: - - **repo_name** – the library name for the repo +- **repo_name** – the library name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 202 Vary: Accept @@ -170,23 +174,25 @@ This is a restricted feature only available to docker admins. "" - Status Codes: +Status Codes: - - **200** – Deleted - - **202** – Accepted - - **400** – Errors (invalid json, missing or invalid fields, etc) - - **401** – Unauthorized - - **403** – Account is not Active +- **200** – Deleted +- **202** – Accepted +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Account is not Active -### Repository Images +# Repository Images -#### User Repo Images +## User Repository Images + +### Update user repository images `PUT /v1/repositories/(namespace)/(repo_name)/images` Update the images for a user repo. - **Example Request**: +**Example Request**: PUT /v1/repositories/foo/bar/images HTTP/1.1 Host: index.docker.io @@ -197,12 +203,12 @@ Update the images for a user repo. [{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f", "checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"}] - Parameters: +Parameters: - - **namespace** – the namespace for the repo - - **repo_name** – the name for the repo +- **namespace** – the namespace for the repo +- **repo_name** – the name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 204 Vary: Accept @@ -210,29 +216,31 @@ Update the images for a user repo. "" - Status Codes: +Status Codes: - - **204** – Created - - **400** – Errors (invalid json, missing or invalid fields, etc) - - **401** – Unauthorized - - **403** – Account is not Active or permission denied +- **204** – Created +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Account is not Active or permission denied + +### List user repository images `GET /v1/repositories/(namespace)/(repo_name)/images` Get the images for a user repo. - **Example Request**: +**Example Request**: GET /v1/repositories/foo/bar/images HTTP/1.1 Host: index.docker.io Accept: application/json - Parameters: +Parameters: - - **namespace** – the namespace for the repo - - **repo_name** – the name for the repo +- **namespace** – the namespace for the repo +- **repo_name** – the name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -243,18 +251,20 @@ Get the images for a user repo. {"id": "ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds", "checksum": "34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew"}] - Status Codes: +Status Codes: - - **200** – OK - - **404** – Not found +- **200** – OK +- **404** – Not found -#### Library Repo Images +## Library Repository Images + +### Update library repository images `PUT /v1/repositories/(repo_name)/images` Update the images for a library repo. - **Example Request**: +**Example Request**: PUT /v1/repositories/foobar/images HTTP/1.1 Host: index.docker.io @@ -265,11 +275,11 @@ Update the images for a library repo. [{"id": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f", "checksum": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"}] - Parameters: +Parameters: - - **repo_name** – the library name for the repo +- **repo_name** – the library name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 204 Vary: Accept @@ -277,28 +287,30 @@ Update the images for a library repo. "" - Status Codes: +Status Codes: - - **204** – Created - - **400** – Errors (invalid json, missing or invalid fields, etc) - - **401** – Unauthorized - - **403** – Account is not Active or permission denied +- **204** – Created +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Account is not Active or permission denied + +### List library repository images `GET /v1/repositories/(repo_name)/images` Get the images for a library repo. - **Example Request**: +**Example Request**: GET /v1/repositories/foobar/images HTTP/1.1 Host: index.docker.io Accept: application/json - Parameters: +Parameters: - - **repo_name** – the library name for the repo +- **repo_name** – the library name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -309,31 +321,33 @@ Get the images for a library repo. {"id": "ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds", "checksum": "34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew"}] - Status Codes: +Status Codes: - - **200** – OK - - **404** – Not found +- **200** – OK +- **404** – Not found -### Repository Authorization +# Repository Authorization -#### Library Repo +## Library Repository + +### Authorize a token for a library `PUT /v1/repositories/(repo_name)/auth` Authorize a token for a library repo - **Example Request**: +**Example Request**: PUT /v1/repositories/foobar/auth HTTP/1.1 Host: index.docker.io Accept: application/json Authorization: Token signature=123abc,repository="library/foobar",access=write - Parameters: +Parameters: - - **repo_name** – the library name for the repo +- **repo_name** – the library name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -341,31 +355,33 @@ Authorize a token for a library repo "OK" - Status Codes: +Status Codes: - - **200** – OK - - **403** – Permission denied - - **404** – Not found +- **200** – OK +- **403** – Permission denied +- **404** – Not found -#### User Repo +## User Repository + +### Authorize a token for a user repository `PUT /v1/repositories/(namespace)/(repo_name)/auth` Authorize a token for a user repo - **Example Request**: +**Example Request**: PUT /v1/repositories/foo/bar/auth HTTP/1.1 Host: index.docker.io Accept: application/json Authorization: Token signature=123abc,repository="foo/bar",access=write - Parameters: +Parameters: - - **namespace** – the namespace for the repo - - **repo_name** – the name for the repo +- **namespace** – the namespace for the repo +- **repo_name** – the name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -373,28 +389,28 @@ Authorize a token for a user repo "OK" - Status Codes: +Status Codes: - - **200** – OK - - **403** – Permission denied - - **404** – Not found +- **200** – OK +- **403** – Permission denied +- **404** – Not found -### Users +## Users -#### User Login +### User Login -`GET /v1/users` +`GET /v1/users/` If you want to check your login, you can try this endpoint - **Example Request**: +**Example Request**: - GET /v1/users HTTP/1.1 + GET /v1/users/ HTTP/1.1 Host: index.docker.io Accept: application/json Authorization: Basic akmklmasadalkm== - **Example Response**: +**Example Response**: HTTP/1.1 200 OK Vary: Accept @@ -402,21 +418,21 @@ If you want to check your login, you can try this endpoint OK - Status Codes: +Status Codes: - - **200** – no error - - **401** – Unauthorized - - **403** – Account is not Active +- **200** – no error +- **401** – Unauthorized +- **403** – Account is not Active -#### User Register +### User Register -`POST /v1/users` +`POST /v1/users/` Registering a new account. - **Example request**: +**Example request**: - POST /v1/users HTTP/1.1 + POST /v1/users/ HTTP/1.1 Host: index.docker.io Accept: application/json Content-Type: application/json @@ -425,16 +441,14 @@ Registering a new account. "password": "toto42", "username": "foobar"} - Json Parameters: +Json Parameters: -   - - - **email** – valid email address, that needs to be confirmed - - **username** – min 4 character, max 30 characters, must match +- **email** – valid email address, that needs to be confirmed +- **username** – min 4 character, max 30 characters, must match the regular expression [a-z0-9_]. - - **password** – min 5 characters +- **password** – min 5 characters - **Example Response**: +**Example Response**: HTTP/1.1 201 OK Vary: Accept @@ -442,25 +456,24 @@ Registering a new account. "User Created" - Status Codes: +Status Codes: - - **201** – User Created - - **400** – Errors (invalid json, missing or invalid fields, etc) +- **201** – User Created +- **400** – Errors (invalid json, missing or invalid fields, etc) -#### Update User +### Update User `PUT /v1/users/(username)/` Change a password or email address for given user. If you pass in an +email, it will add it to your account, it will not remove the old +one. Passwords will be updated. - email, it will add it to your account, it will not remove the old - one. Passwords will be updated. +It is up to the client to verify that that password that is sent is +the one that they want. Common approach is to have them type it +twice. - It is up to the client to verify that that password that is sent is - the one that they want. Common approach is to have them type it - twice. - - **Example Request**: +**Example Request**: PUT /v1/users/fakeuser/ HTTP/1.1 Host: index.docker.io @@ -471,11 +484,11 @@ Change a password or email address for given user. If you pass in an {"email": "sam@docker.com", "password": "toto42"} - Parameters: +Parameters: - - **username** – username for the person you want to update +- **username** – username for the person you want to update - **Example Response**: +**Example Response**: HTTP/1.1 204 Vary: Accept @@ -483,20 +496,18 @@ Change a password or email address for given user. If you pass in an "" - Status Codes: +Status Codes: - - **204** – User Updated - - **400** – Errors (invalid json, missing or invalid fields, etc) - - **401** – Unauthorized - - **403** – Account is not Active - - **404** – User not found +- **204** – User Updated +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Account is not Active +- **404** – User not found ## Search If you need to search the index, this is the endpoint you would use. -### Search - `GET /v1/search` Search the Index given a search term. It accepts @@ -504,13 +515,13 @@ Search the Index given a search term. It accepts [GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3) only. - **Example request**: +**Example request**: GET /v1/search?q=search_term HTTP/1.1 - Host: example.com + Host: index.docker.io Accept: application/json - **Example response**: +**Example response**: HTTP/1.1 200 OK Vary: Accept @@ -525,11 +536,11 @@ Search the Index given a search term. It accepts ] } - Query Parameters: +Query Parameters: - - **q** – what you want to search for +- **q** – what you want to search for - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error diff --git a/docs/sources/reference/api/docker_io_accounts_api.md b/docs/sources/reference/api/docker_io_accounts_api.md index b9f76ba92c..efb86eb33a 100644 --- a/docs/sources/reference/api/docker_io_accounts_api.md +++ b/docs/sources/reference/api/docker_io_accounts_api.md @@ -4,43 +4,39 @@ page_keywords: API, Docker, accounts, REST, documentation # docker.io Accounts API -## 1. Endpoints - -### 1.1 Get a single user +## Get a single user `GET /api/v1.1/users/:username/` Get profile info for the specified user. - Parameters: +Parameters: - - **username** – username of the user whose profile info is being +- **username** – username of the user whose profile info is being requested. - Request Headers: +Request Headers: -   - - - **Authorization** – required authentication credentials of +- **Authorization** – required authentication credentials of either type HTTP Basic or OAuth Bearer Token. - Status Codes: +Status Codes: - - **200** – success, user data returned. - - **401** – authentication error. - - **403** – permission error, authenticated user must be the user +- **200** – success, user data returned. +- **401** – authentication error. +- **403** – permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have `profile_read` scope. - - **404** – the specified username does not exist. +- **404** – the specified username does not exist. - **Example request**: +**Example request**: GET /api/v1.1/users/janedoe/ HTTP/1.1 Host: www.docker.io Accept: application/json Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -60,48 +56,44 @@ Get profile info for the specified user. "is_active": true } -### 1.2 Update a single user +## Update a single user `PATCH /api/v1.1/users/:username/` Update profile info for the specified user. - Parameters: +Parameters: - - **username** – username of the user whose profile info is being +- **username** – username of the user whose profile info is being updated. - Json Parameters: +Json Parameters: -   - - - **full_name** (*string*) – (optional) the new name of the user. - - **location** (*string*) – (optional) the new location. - - **company** (*string*) – (optional) the new company of the user. - - **profile_url** (*string*) – (optional) the new profile url. - - **gravatar_email** (*string*) – (optional) the new Gravatar +- **full_name** (*string*) – (optional) the new name of the user. +- **location** (*string*) – (optional) the new location. +- **company** (*string*) – (optional) the new company of the user. +- **profile_url** (*string*) – (optional) the new profile url. +- **gravatar_email** (*string*) – (optional) the new Gravatar email address. - Request Headers: +Request Headers: -   - - - **Authorization** – required authentication credentials of +- **Authorization** – required authentication credentials of either type HTTP Basic or OAuth Bearer Token. - - **Content-Type** – MIME Type of post data. JSON, url-encoded +- **Content-Type** – MIME Type of post data. JSON, url-encoded form data, etc. - Status Codes: +Status Codes: - - **200** – success, user data updated. - - **400** – post data validation error. - - **401** – authentication error. - - **403** – permission error, authenticated user must be the user +- **200** – success, user data updated. +- **400** – post data validation error. +- **401** – authentication error. +- **403** – permission error, authenticated user must be the user whose data is being updated, OAuth access tokens must have `profile_write` scope. - - **404** – the specified username does not exist. +- **404** – the specified username does not exist. - **Example request**: +**Example request**: PATCH /api/v1.1/users/janedoe/ HTTP/1.1 Host: www.docker.io @@ -114,7 +106,7 @@ Update profile info for the specified user. "company": "Retired", } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -134,41 +126,39 @@ Update profile info for the specified user. "is_active": true } -### 1.3 List email addresses for a user +## List email addresses for a user `GET /api/v1.1/users/:username/emails/` List email info for the specified user. - Parameters: +Parameters: - - **username** – username of the user whose profile info is being +- **username** – username of the user whose profile info is being updated. - Request Headers: +Request Headers: -   - - - **Authorization** – required authentication credentials of +- **Authorization** – required authentication credentials of either type HTTP Basic or OAuth Bearer Token - Status Codes: +Status Codes: - - **200** – success, user data updated. - - **401** – authentication error. - - **403** – permission error, authenticated user must be the user +- **200** – success, user data updated. +- **401** – authentication error. +- **403** – permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have `email_read` scope. - - **404** – the specified username does not exist. +- **404** – the specified username does not exist. - **Example request**: +**Example request**: GET /api/v1.1/users/janedoe/emails/ HTTP/1.1 Host: www.docker.io Accept: application/json Authorization: Bearer zAy0BxC1wDv2EuF3tGs4HrI6qJp6KoL7nM - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -181,7 +171,7 @@ List email info for the specified user. } ] -### 1.4 Add email address for a user +## Add email address for a user `POST /api/v1.1/users/:username/emails/` @@ -189,32 +179,28 @@ Add a new email address to the specified user's account. The email address must be verified separately, a confirmation email is not automatically sent. - Json Parameters: +Json Parameters: -   +- **email** (*string*) – email address to be added. - - **email** (*string*) – email address to be added. +Request Headers: - Request Headers: - -   - - - **Authorization** – required authentication credentials of +- **Authorization** – required authentication credentials of either type HTTP Basic or OAuth Bearer Token. - - **Content-Type** – MIME Type of post data. JSON, url-encoded +- **Content-Type** – MIME Type of post data. JSON, url-encoded form data, etc. - Status Codes: +Status Codes: - - **201** – success, new email added. - - **400** – data validation error. - - **401** – authentication error. - - **403** – permission error, authenticated user must be the user +- **201** – success, new email added. +- **400** – data validation error. +- **401** – authentication error. +- **403** – permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have `email_write` scope. - - **404** – the specified username does not exist. +- **404** – the specified username does not exist. - **Example request**: +**Example request**: POST /api/v1.1/users/janedoe/emails/ HTTP/1.1 Host: www.docker.io @@ -226,7 +212,7 @@ automatically sent. "email": "jane.doe+other@example.com" } - **Example response**: +**Example response**: HTTP/1.1 201 Created Content-Type: application/json @@ -237,40 +223,36 @@ automatically sent. "primary": false } -### 1.5 Delete email address for a user +## Delete email address for a user `DELETE /api/v1.1/users/:username/emails/` Delete an email address from the specified user's account. You cannot delete a user's primary email address. - Json Parameters: +Json Parameters: -   +- **email** (*string*) – email address to be deleted. - - **email** (*string*) – email address to be deleted. +Request Headers: - Request Headers: - -   - - - **Authorization** – required authentication credentials of +- **Authorization** – required authentication credentials of either type HTTP Basic or OAuth Bearer Token. - - **Content-Type** – MIME Type of post data. JSON, url-encoded +- **Content-Type** – MIME Type of post data. JSON, url-encoded form data, etc. - Status Codes: +Status Codes: - - **204** – success, email address removed. - - **400** – validation error. - - **401** – authentication error. - - **403** – permission error, authenticated user must be the user +- **204** – success, email address removed. +- **400** – validation error. +- **401** – authentication error. +- **403** – permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have `email_write` scope. - - **404** – the specified username or email address does not +- **404** – the specified username or email address does not exist. - **Example request**: +**Example request**: DELETE /api/v1.1/users/janedoe/emails/ HTTP/1.1 Host: www.docker.io @@ -282,7 +264,7 @@ cannot delete a user's primary email address. "email": "jane.doe+other@example.com" } - **Example response**: +**Example response**: HTTP/1.1 204 NO CONTENT Content-Length: 0 diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index e712f864f2..39c83743bf 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -4,9 +4,12 @@ page_keywords: API, Docker, rcli, REST, documentation # Docker Remote API - - The Remote API is replacing `rcli`. - By default the Docker daemon listens on `unix:///var/run/docker.sock` and the client must have `root` access to interact with the daemon. + - If the Docker daemon is set to use an encrypted TCP socket (`--tls`, + or `--tlsverify`) as with Boot2Docker 1.3.0, then you need to add extra + parameters to `curl` when making test API requests: + `curl --insecure --cert ~/.docker/cert.pem --key ~/.docker/key.pem https://boot2docker:2376/images/json` - If a group named `docker` exists on your system, docker will apply ownership of the socket to the group. - The API tends to be REST, but for some complex commands, like attach @@ -15,16 +18,31 @@ page_keywords: API, Docker, rcli, REST, documentation - Since API version 1.2, the auth configuration is now handled client side, so the client has to send the `authConfig` as a `POST` in `/images/(name)/push`. - authConfig, set as the `X-Registry-Auth` header, is currently a Base64 - encoded (JSON) string with credentials: - `{'username': string, 'password': string, 'email': string, 'serveraddress' : string}` + encoded (JSON) string with the following structure: + `{"username": "string", "password": "string", "email": "string", + "serveraddress" : "string", "auth": ""}`. Notice that `auth` is to be left + empty, `serveraddress` is a domain/ip without protocol, and that double + quotes (instead of single ones) are required. + - The Remote API uses an open schema model. In this model, unknown + properties in incoming messages will be ignored. + Client applications need to take this into account to ensure + they will not break when talking to newer Docker daemons. -The current version of the API is v1.14 +The current version of the API is v1.15 Calling `/info` is the same as calling -`/v1.14/info`. +`/v1.15/info`. You can still call an old version of the API using -`/v1.13/info`. +`/v1.14/info`. + +## v1.15 + +### Full Documentation + +[*Docker Remote API v1.15*](/reference/api/docker_remote_api_v1.15/) + +### What's new ## v1.14 diff --git a/docs/sources/reference/api/docker_remote_api_v1.0.md b/docs/sources/reference/api/docker_remote_api_v1.0.md index 197991de94..3d8eedacf7 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.0.md +++ b/docs/sources/reference/api/docker_remote_api_v1.0.md @@ -22,11 +22,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -62,24 +62,22 @@ List containers } ] - Query Parameters: +Query Parameters: -   - - - **all** – 1/True/true or 0/False/false, Show all containers. +- **all** – 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default - - **limit** – Show `limit` last created +- **limit** – Show `limit` last created containers, include non-running ones. - - **since** – Show only containers created since Id, include +- **since** – Show only containers created since Id, include non-running ones. - - **before** – Show only containers created before Id, include +- **before** – Show only containers created before Id, include non-running ones. - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -87,7 +85,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -114,9 +112,9 @@ Create a container "VolumesFrom":"" } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -124,18 +122,16 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Status Codes: - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -144,11 +140,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -199,11 +195,11 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -211,11 +207,11 @@ Return low-level information on the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -235,11 +231,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -247,22 +243,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -270,19 +266,19 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/start HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -290,25 +286,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -316,25 +310,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -342,19 +334,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -362,38 +354,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Wait a container @@ -401,22 +391,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -424,27 +414,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -454,11 +442,11 @@ Remove the container `id` from the filesystem List images `format` could be json or viz (json default) - **Example request**: +**Example request**: GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -478,11 +466,11 @@ List images `format` could be json or viz (json default) } ] - **Example request**: +**Example request**: GET /images/viz HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: text/plain @@ -505,50 +493,46 @@ List images `format` could be json or viz (json default) base [style=invisible] } - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=ubuntu HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -556,27 +540,25 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK - {{ STREAM }} + {{ TAR STREAM }} - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -584,11 +566,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/centos/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -620,11 +602,11 @@ Return low-level information on the image `name` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -632,11 +614,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/fedora/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -654,11 +636,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -677,11 +659,11 @@ Push the image `name` on the registry {{ STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -689,27 +671,26 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **500** – server error ### Remove an image @@ -717,19 +698,19 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such image - - **500** – server error +- **204** – no error +- **404** – no such image +- **500** – server error ### Search images @@ -737,11 +718,11 @@ Remove the image `name` from the filesystem Search for an image on [Docker Hub](https://hub.docker.com) - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -773,29 +754,27 @@ Search for an image on [Docker Hub](https://hub.docker.com) Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **t** – repository name to be applied to the resulting image in +- **t** – repository name to be applied to the resulting image in case of success - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get default username and email @@ -803,11 +782,11 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: GET /auth HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -817,18 +796,18 @@ Get the default username and email "email":"hannibal@a-team.com" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error -### Check auth configuration and store it +### Check auth configuration and store i `POST /auth` Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -839,15 +818,16 @@ Get the default username and email "email":"hannibal@a-team.com" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -855,11 +835,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -874,10 +854,10 @@ Display system-wide information "SwapLimit":false } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -885,11 +865,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -900,10 +880,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -921,50 +901,48 @@ Create a new image from a container's changes "PortSpecs":["22"] } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error # 3. Going further ## 3.1 Inside `docker run` -Here are the steps of `docker run` : +As an example, the `docker run` command line makes the following API calls: - - Create the container +- Create the container - - If the status code is 404, it means the image doesn't exists: - - Try to pull it - - Then retry to create the container +- If the status code is 404, it means the image doesn't exist: + - Try to pull it + - Then retry to create the container - - Start the container +- Start the container - - If you are not in detached mode: - - Attach to the container, using logs=1 (to have stdout and - stderr from the container's start) and stream=1 +- If you are not in detached mode: + - Attach to the container, using logs=1 (to have stdout and + stderr from the container's start) and stream=1 - - If in detached mode or only stdin is attached: - - Display the container's +- If in detached mode or only stdin is attached: + - Display the container's ## 3.2 Hijacking diff --git a/docs/sources/reference/api/docker_remote_api_v1.1.md b/docs/sources/reference/api/docker_remote_api_v1.1.md index 928e3210f2..705544bd9b 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.1.md +++ b/docs/sources/reference/api/docker_remote_api_v1.1.md @@ -22,11 +22,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -62,24 +62,22 @@ List containers } ] - Query Parameters: +Query Parameters: -   - - - **all** – 1/True/true or 0/False/false, Show all containers. +- **all** – 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default - - **limit** – Show `limit` last created +- **limit** – Show `limit` last created containers, include non-running ones. - - **since** – Show only containers created since Id, include +- **since** – Show only containers created since Id, include non-running ones. - - **before** – Show only containers created before Id, include +- **before** – Show only containers created before Id, include non-running ones. - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -87,7 +85,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -114,9 +112,9 @@ Create a container "VolumesFrom":"" } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -124,18 +122,16 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Status Codes: - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -144,11 +140,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -199,11 +195,11 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -211,11 +207,11 @@ Return low-level information on the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -235,11 +231,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -247,22 +243,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream {{ STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -270,19 +266,19 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/start HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -290,25 +286,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -316,25 +310,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -342,19 +334,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -362,38 +354,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Wait a container @@ -401,22 +391,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -424,27 +414,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -454,11 +442,11 @@ Remove the container `id` from the filesystem List images `format` could be json or viz (json default) - **Example request**: +**Example request**: GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -478,11 +466,11 @@ List images `format` could be json or viz (json default) } ] - **Example request**: +**Example request**: GET /images/viz HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: text/plain @@ -505,30 +493,28 @@ List images `format` could be json or viz (json default) base [style=invisible] } - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=ubuntu HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -538,20 +524,18 @@ Create an image, either by pull it from the registry or by importing it {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -559,11 +543,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -573,17 +557,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -591,11 +573,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/centos/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -627,11 +609,11 @@ Return low-level information on the image `name` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -639,11 +621,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/fedora/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -661,11 +643,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -687,11 +669,11 @@ Push the image `name` on the registry {"error":"Invalid..."} ... - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -699,28 +681,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -728,19 +709,19 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such image - - **500** – server error +- **204** – no error +- **404** – no such image +- **500** – server error ### Search images @@ -748,11 +729,11 @@ Remove the image `name` from the filesystem Search for an image on [Docker Hub](https://hub.docker.com) - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -784,29 +765,29 @@ Search for an image on [Docker Hub](https://hub.docker.com) Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 {{ STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK {{ STREAM }} - Query Parameters: +Query Parameters:   - - **t** – tag to be applied to the resulting image in case of +- **t** – tag to be applied to the resulting image in case of success - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get default username and email @@ -814,11 +795,11 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: GET /auth HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -828,18 +809,18 @@ Get the default username and email "email":"hannibal@a-team.com" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error -### Check auth configuration and store it +### Check auth configuration and store i `POST /auth` Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -850,15 +831,16 @@ Get the default username and email "email":"hannibal@a-team.com" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -866,11 +848,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -885,10 +867,10 @@ Display system-wide information "SwapLimit":false } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -896,11 +878,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -911,10 +893,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -922,7 +904,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -932,29 +914,27 @@ Create a new image from a container's changes "PortSpecs":["22"] } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error # 3. Going further @@ -964,7 +944,7 @@ Here are the steps of `docker run` : - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -975,7 +955,7 @@ Here are the steps of `docker run` : stderr from the container's start) and stream=1 - If in detached mode or only stdin is attached: - - Display the container's + - Display the container's ## 3.2 Hijacking diff --git a/docs/sources/reference/api/docker_remote_api_v1.10.md b/docs/sources/reference/api/docker_remote_api_v1.10.md index 6ffae3e07e..52bbe2e486 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.10.md +++ b/docs/sources/reference/api/docker_remote_api_v1.10.md @@ -23,11 +23,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -75,26 +75,22 @@ List containers } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -102,7 +98,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -134,9 +130,9 @@ Create a container } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -144,25 +140,23 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: + +- **config** – the container's configuration + +Query Parameters:   - - **config** – the container's configuration - - Query Parameters: - -   - - - **name** – Assign the specified name to the container. Must +- **name** – Assign the specified name to the container. Mus match `/?[a-zA-Z0-9_-]+`. - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -170,11 +164,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -241,11 +235,11 @@ Return low-level information on the container `id` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -253,11 +247,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -282,17 +276,17 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters:   - - **ps\_args** – ps arguments to use (e.g., aux) +- **ps\_args** – ps arguments to use (e.g., aux) - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -300,11 +294,11 @@ List processes running inside the container `id` Inspect changes on container `id` 's filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -324,11 +318,11 @@ Inspect changes on container `id` 's filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -336,22 +330,22 @@ Inspect changes on container `id` 's filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -359,14 +353,14 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts":false, "Privileged":false @@ -374,22 +368,22 @@ Start the container `id` "VolumesFrom": ["parent", "other:ro"] } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -397,25 +391,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -423,25 +415,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -449,24 +439,24 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters +Query Parameters - - **signal** - Signal to send to the container: integer or string like "SIGINT". - When not set, SIGKILL is assumed and the call will waits for the container to exit. +- **signal** - Signal to send to the container: integer or string like "SIGINT". + When not set, SIGKILL is assumed and the call will wait for the container to exit. - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -474,38 +464,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -530,9 +518,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -558,51 +546,49 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container `DELETE /containers/(id*) : Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - - **force** – 1/True/true or 0/False/false, Removes the container +- **force** – 1/True/true or 0/False/false, Removes the container even if it was running. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -610,7 +596,7 @@ Block until container `id` stops, then returns Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -619,18 +605,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### 2.2 Images @@ -642,7 +628,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -677,13 +663,13 @@ Copy files or folders of container `id` `POST /images/create` Create an image, either by pull it from the registry or by importing - it + i - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -697,26 +683,22 @@ Create an image, either by pull it from the registry or by importing `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -725,11 +707,11 @@ Create an image, either by pull it from the registry or by importing Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -739,17 +721,15 @@ Insert a file from `url` in the image {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -757,11 +737,11 @@ Insert a file from `url` in the image Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -793,11 +773,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -805,11 +785,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -827,11 +807,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -839,11 +819,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -854,32 +834,27 @@ Push the image `name` on the registry ... If you wish to push an image on to a private registry, that image must already have been tagged - into a repository which references that registry host name and port. This repository name should + into a repository which references that registry host name and port. This repository name should then be used in the URL. This mirrors the flow of the CLI. - **Example request**: +**Example request**: - POST /images/registry.acme.com:5000/test/push HTTP/1.1 - + POST /images/registry.acme.com:5000/test/push HTTP/1.1 - Query Parameters: -   +Query Parameters: - - **tag** – the tag to associate with the image on the registry, optional +- **tag** – the tag to associate with the image on the registry, optional - Request Headers: +Request Headers: -   +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - - **X-Registry-Auth** – include a base64-encoded AuthConfig - object. +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -887,39 +862,38 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image `DELETE /images/(name*) : Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -930,19 +904,17 @@ Tag the image `name` into a repository {"Deleted":"53b4f83ac9"} ] - Query Parameters: +Query Parameters: -   +- **force** – 1/True/true or 0/False/false, default false +- **noprune** – 1/True/true or 0/False/false, default false - - **force** – 1/True/true or 0/False/false, default false - - **noprune** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -954,11 +926,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -988,16 +960,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### 2.3 Misc @@ -1007,13 +977,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1030,28 +1000,23 @@ Build an image from Dockerfile via stdin which will be accessible in the build context (See the [*ADD build command*](/reference/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image - - **rm** - remove intermediate containers after a successful build +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** - remove intermediate containers after a successful build Request Headers: -   +- **Content-type** – should be set to `"application/tar"`. +- **X-Registry-Config** – base64-encoded ConfigFile objec - - **Content-type** – should be set to - `"application/tar"`. - - **X-Registry-Config** – base64-encoded ConfigFile object +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1059,7 +1024,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1071,15 +1036,16 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1087,11 +1053,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1107,10 +1073,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1118,11 +1084,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1133,10 +1099,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -1144,7 +1110,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -1175,7 +1141,7 @@ Create a new image from a container's changes } } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream @@ -1183,41 +1149,47 @@ Create a new image from a container's changes {"Id":"596069db4bf5"} - Json Parameters: +Json Parameters: - - **config** - the container's configuration +- **config** - the container's configuration - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, or via - polling (using since) +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1227,16 +1199,14 @@ Get events from docker, either in real time via streaming, or via {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1245,21 +1215,23 @@ Get events from docker, either in real time via streaming, or via Get a tarball containing all images and metadata for the repository specified by `name`. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/x-tar Binary data stream - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Load a tarball with a set of images and tags into docker @@ -1267,20 +1239,43 @@ Get a tarball containing all images and metadata for the repository Load a set of images and tags into the docker repository. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** POST /images/load Tarball in body - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` # 3. Going further @@ -1290,7 +1285,7 @@ Here are the steps of `docker run` : - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1311,6 +1306,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.11.md b/docs/sources/reference/api/docker_remote_api_v1.11.md index a0187dbdf6..2368daf4ec 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.11.md +++ b/docs/sources/reference/api/docker_remote_api_v1.11.md @@ -23,11 +23,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -75,26 +75,22 @@ List containers } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -102,7 +98,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -135,9 +131,9 @@ Create a container } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -145,25 +141,21 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Query Parameters: - Query Parameters: - -   - - - **name** – Assign the specified name to the container. Must +- **name** – Assign the specified name to the container. Mus match `/?[a-zA-Z0-9_-]+`. - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -172,11 +164,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -245,11 +237,11 @@ Return low-level information on the container `id` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -257,11 +249,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -286,17 +278,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Get container logs @@ -304,35 +294,35 @@ List processes running inside the container `id` Get stdout and stderr logs from the container ``id`` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters:   - - **follow** – 1/True/true or 0/False/false, return stream. +- **follow** – 1/True/true or 0/False/false, return stream. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log. Default false - - **timestamps** – 1/True/true or 0/False/false, if logs=true, print +- **timestamps** – 1/True/true or 0/False/false, if logs=true, prin timestamps for every log line. Default false - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -340,11 +330,11 @@ Get stdout and stderr logs from the container ``id`` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -364,11 +354,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -376,22 +366,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -399,14 +389,14 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts":false, "Privileged":false, @@ -414,22 +404,22 @@ Start the container `id` "VolumesFrom": ["parent", "other:ro"] } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -437,25 +427,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -463,25 +451,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -489,24 +475,24 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters +Query Parameters - - **signal** - Signal to send to the container: integer or string like "SIGINT". - When not set, SIGKILL is assumed and the call will waits for the container to exit. +- **signal** - Signal to send to the container: integer or string like "SIGINT". + When not set, SIGKILL is assumed and the call will wait for the container to exit. - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -514,38 +500,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -570,9 +554,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -597,22 +581,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -620,29 +604,27 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - - **force** – 1/True/true or 0/False/false, Removes the container +- **force** – 1/True/true or 0/False/false, Removes the container even if it was running. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -650,7 +632,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -659,18 +641,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -682,7 +664,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -716,13 +698,13 @@ Copy files or folders of container `id` `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -736,26 +718,22 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -763,11 +741,11 @@ Create an image, either by pull it from the registry or by importing it Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -801,11 +779,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -813,11 +791,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -835,11 +813,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -847,11 +825,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -862,32 +840,27 @@ Push the image `name` on the registry ... If you wish to push an image on to a private registry, that image must already have been tagged - into a repository which references that registry host name and port. This repository name should + into a repository which references that registry host name and port. This repository name should then be used in the URL. This mirrors the flow of the CLI. - **Example request**: +**Example request**: - POST /images/registry.acme.com:5000/test/push HTTP/1.1 - + POST /images/registry.acme.com:5000/test/push HTTP/1.1 - Query Parameters: -   +Query Parameters: - - **tag** – the tag to associate with the image on the registry, optional +- **tag** – the tag to associate with the image on the registry, optional - Request Headers: +Request Headers: -   +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - - **X-Registry-Auth** – include a base64-encoded AuthConfig - object. +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -895,28 +868,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -924,11 +896,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -939,19 +911,17 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Query Parameters: +Query Parameters: -   +- **force** – 1/True/true or 0/False/false, default false +- **noprune** – 1/True/true or 0/False/false, default false - - **force** – 1/True/true or 0/False/false, default false - - **noprune** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -963,11 +933,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -997,16 +967,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -1016,13 +984,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1039,28 +1007,23 @@ Build an image from Dockerfile via stdin which will be accessible in the build context (See the [*ADD build command*](/reference/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image - - **rm** - remove intermediate containers after a successful build +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** - remove intermediate containers after a successful build Request Headers: -   +- **Content-type** – should be set to `"application/tar"`. +- **X-Registry-Config** – base64-encoded ConfigFile objec - - **Content-type** – should be set to - `"application/tar"`. - - **X-Registry-Config** – base64-encoded ConfigFile object +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1068,7 +1031,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1080,15 +1043,15 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1096,11 +1059,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1122,10 +1085,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1133,11 +1096,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1148,10 +1111,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Ping the docker server @@ -1159,20 +1122,21 @@ Show the docker version information Ping the docker server - **Example request**: +**Example request**: GET /_ping HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain OK - Status Codes: +Status Codes: - - **200** - no error - - **500** - server error +- **200** - no error +- **500** - server error ### Create a new image from a container's changes @@ -1180,7 +1144,7 @@ Ping the docker server Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -1211,48 +1175,52 @@ Create a new image from a container's changes } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 Created + Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Json Parameters: +Json Parameters: +- **config** - the container's configuration +Query Parameters: - - **config** - the container's configuration +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith + <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Query Parameters: +Status Codes: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith - <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - - Status Codes: - - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` -Get events from docker, either in real time via streaming, or -via polling (using since) +Get container events from docker, either in real time via streaming, or via +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1262,17 +1230,15 @@ via polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling +- **until** – timestamp used for polling - - **since** – timestamp used for polling - - **until** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1281,21 +1247,23 @@ via polling (using since) Get a tarball containing all images and metadata for the repository specified by `name`. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/x-tar Binary data stream - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Load a tarball with a set of images and tags into docker @@ -1303,30 +1271,53 @@ specified by `name`. Load a set of images and tags into the docker repository. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** POST /images/load Tarball in body - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` # 3. Going further ## 3.1 Inside `docker run` -Here are the steps of `docker run`: +As an example, the `docker run` command line makes the following API calls: - Create the container -- If the status code is 404, it means the image doesn't exists: +- If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1347,6 +1338,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.12.md b/docs/sources/reference/api/docker_remote_api_v1.12.md index 9ea83e2853..8b245f5e9c 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.12.md +++ b/docs/sources/reference/api/docker_remote_api_v1.12.md @@ -8,8 +8,8 @@ page_keywords: API, Docker, rcli, REST, documentation - The Remote API has replaced `rcli`. - The daemon listens on `unix:///var/run/docker.sock` but you can - [*Bind Docker to another host/port or a Unix socket*]( - /use/basics/#bind-docker). + [Bind Docker to another host/port or a Unix socket]( + /articles/basics/#bind-docker-to-another-hostport-or-a-unix-socket). - The API tends to be REST, but for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `STDOUT`, `STDIN` and `STDERR`. @@ -24,11 +24,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -76,28 +76,26 @@ List containers } ] - Query Parameters: +Query Parameters: -   - - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul +- **limit** – Show `limit` last created containers, include non-running ones. - - **since** – Show only containers created since Id, include +- **since** – Show only containers created since Id, include non-running ones. - - **before** – Show only containers created before Id, include +- **before** – Show only containers created before Id, include non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers +- **size** – 1/True/true or 0/False/false, Show the containers sizes - - **filters** – a JSON encoded value of the filters (a map[string][]string) +- **filters** – a JSON encoded value of the filters (a map[string][]string) to process on the images list. - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -105,16 +103,19 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json { "Hostname":"", + "Domainname": "", "User":"", "Memory":0, "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, @@ -131,15 +132,15 @@ Create a container "/tmp": {} }, "WorkingDir":"", - "DisableNetwork": false, + "NetworkDisabled": false, "ExposedPorts":{ "22/tcp": {} } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -147,25 +148,23 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: + +- **config** – the container's configuration + +Query Parameters:   - - **config** – the container's configuration - - Query Parameters: - -   - - - **name** – Assign the specified name to the container. Must +- **name** – Assign the specified name to the container. Mus match `/?[a-zA-Z0-9_-]+`. - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -174,11 +173,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -247,11 +246,11 @@ Return low-level information on the container `id` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -259,11 +258,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -288,17 +287,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Get container logs @@ -306,35 +303,35 @@ List processes running inside the container `id` Get stdout and stderr logs from the container ``id`` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters:   - - **follow** – 1/True/true or 0/False/false, return stream. +- **follow** – 1/True/true or 0/False/false, return stream. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log. Default false - - **timestamps** – 1/True/true or 0/False/false, if logs=true, print +- **timestamps** – 1/True/true or 0/False/false, if logs=true, prin timestamps for every log line. Default false - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -342,11 +339,11 @@ Get stdout and stderr logs from the container ``id`` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -366,11 +363,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -378,22 +375,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -401,7 +398,7 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json @@ -409,7 +406,7 @@ Start the container `id` { "Binds":["/tmp:/tmp"], "Links":["redis3:redis"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts":false, "Privileged":false, @@ -417,22 +414,22 @@ Start the container `id` "VolumesFrom": ["parent", "other:ro"] } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -440,25 +437,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -466,25 +461,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -492,24 +485,24 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters +Query Parameters - - **signal** - Signal to send to the container: integer or string like "SIGINT". - When not set, SIGKILL is assumed and the call will waits for the container to exit. +- **signal** - Signal to send to the container: integer or string like "SIGINT". + When not set, SIGKILL is assumed and the call will wait for the container to exit. - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Pause a container @@ -517,19 +510,19 @@ Kill the container `id` Pause the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/pause HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Unpause a container @@ -537,19 +530,19 @@ Pause the container `id` Unpause the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/unpause HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -557,38 +550,34 @@ Unpause the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   +- **logs** – 1/True/true or 0/False/false, return logs. Default false +- **stream** – 1/True/true or 0/False/false, return stream. Default false +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. + Default false +- **stdout** – 1/True/true or 0/False/false, if logs=true, return + stdout log, if stream=true, attach to stdout. Default false +- **stderr** – 1/True/true or 0/False/false, if logs=true, return + stderr log, if stream=true, attach to stderr. Default false - - **logs** – 1/True/true or 0/False/false, return logs. Default - false - - **stream** – 1/True/true or 0/False/false, return stream. - Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach - to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return - stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return - stderr log, if stream=true, attach to stderr. Default false +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -613,9 +602,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -632,7 +621,7 @@ Attach to the container `id` 2. chose stdout or stderr depending on the first byte 3. Extract the frame size from the last 4 byets 4. Read the extracted size and output it on the correct output - 5. Goto 1) + 5. Goto 1 ### Wait a container @@ -640,22 +629,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -663,29 +652,27 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - - **force** – 1/True/true or 0/False/false, Removes the container +- **force** – 1/True/true or 0/False/false, Removes the container even if it was running. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -693,7 +680,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -702,18 +689,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -725,7 +712,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -756,12 +743,12 @@ Copy files or folders of container `id` ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, default false - - **filters** – a JSON encoded value of the filters (a map[string][]string) to process on the images list. +- **all** – 1/True/true or 0/False/false, default false +- **filters** – a JSON encoded value of the filters (a map[string][]string) to process on the images list. @@ -769,13 +756,13 @@ Copy files or folders of container `id` `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -789,26 +776,22 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error @@ -818,11 +801,11 @@ Create an image, either by pull it from the registry or by importing it Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -856,11 +839,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -868,11 +851,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -890,11 +873,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -902,11 +885,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -917,32 +900,27 @@ Push the image `name` on the registry ... If you wish to push an image on to a private registry, that image must already have been tagged - into a repository which references that registry host name and port. This repository name should + into a repository which references that registry host name and port. This repository name should then be used in the URL. This mirrors the flow of the CLI. - **Example request**: +**Example request**: - POST /images/registry.acme.com:5000/test/push HTTP/1.1 - + POST /images/registry.acme.com:5000/test/push HTTP/1.1 - Query Parameters: -   +Query Parameters: - - **tag** – the tag to associate with the image on the registry, optional +- **tag** – the tag to associate with the image on the registry, optional - Request Headers: +Request Headers: -   +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - - **X-Registry-Auth** – include a base64-encoded AuthConfig - object. +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -950,28 +928,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -979,11 +956,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -994,19 +971,17 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Query Parameters: +Query Parameters: -   +- **force** – 1/True/true or 0/False/false, default false +- **noprune** – 1/True/true or 0/False/false, default false - - **force** – 1/True/true or 0/False/false, default false - - **noprune** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -1018,11 +993,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1052,16 +1027,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -1071,13 +1044,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1094,29 +1067,24 @@ Build an image from Dockerfile via stdin which will be accessible in the build context (See the [*ADD build command*](/reference/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image - - **rm** - remove intermediate containers after a successful build (default behavior) - - **forcerm - always remove intermediate containers (includes rm) +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** - remove intermediate containers after a successful build (default behavior) +- **forcerm - always remove intermediate containers (includes rm) Request Headers: -   +- **Content-type** – should be set to `"application/tar"`. +- **X-Registry-Config** – base64-encoded ConfigFile objec - - **Content-type** – should be set to - `"application/tar"`. - - **X-Registry-Config** – base64-encoded ConfigFile object +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1124,7 +1092,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1136,15 +1104,15 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1152,11 +1120,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1178,10 +1146,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1189,11 +1157,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1205,10 +1173,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Ping the docker server @@ -1216,20 +1184,21 @@ Show the docker version information Ping the docker server - **Example request**: +**Example request**: GET /_ping HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain OK - Status Codes: +Status Codes: - - **200** - no error - - **500** - server error +- **200** - no error +- **500** - server error ### Create a new image from a container's changes @@ -1237,16 +1206,19 @@ Ping the docker server Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json { "Hostname":"", + "Domainname": "", "User":"", "Memory":0, "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, @@ -1262,54 +1234,58 @@ Create a new image from a container's changes "/tmp": {} }, "WorkingDir":"", - "DisableNetwork": false, + "NetworkDisabled": false, "ExposedPorts":{ "22/tcp": {} } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 Created + Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Json Parameters: +Json Parameters: +- **config** - the container's configuration +Query Parameters: - - **config** - the container's configuration +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith + <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Query Parameters: +Status Codes: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith - <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - - Status Codes: - - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` -Get events from docker, either in real time via streaming, or -via polling (using since) +Get container events from docker, either in real time via streaming, or via +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1319,17 +1295,15 @@ via polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling +- **until** – timestamp used for polling - - **since** – timestamp used for polling - - **until** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1338,52 +1312,76 @@ via polling (using since) Get a tarball containing all images and metadata for the repository specified by `name`. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/x-tar Binary data stream - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Load a tarball with a set of images and tags into docker `POST /images/load` Load a set of images and tags into the docker repository. +See the [image tarball format](#image-tarball-format) for more details. - **Example request** +**Example request** POST /images/load Tarball in body - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` # 3. Going further ## 3.1 Inside `docker run` -Here are the steps of `docker run`: +As an example, the `docker run` command line makes the following API calls: - Create the container -- If the status code is 404, it means the image doesn't exists: +- If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1404,6 +1402,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.13.md b/docs/sources/reference/api/docker_remote_api_v1.13.md index d782391476..1a25da18ae 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.13.md +++ b/docs/sources/reference/api/docker_remote_api_v1.13.md @@ -8,8 +8,8 @@ page_keywords: API, Docker, rcli, REST, documentation - The Remote API has replaced `rcli`. - The daemon listens on `unix:///var/run/docker.sock` but you can - [*Bind Docker to another host/port or a Unix socket*]( - /use/basics/#bind-docker). + [Bind Docker to another host/port or a Unix socket]( + /articles/basics/#bind-docker-to-another-hostport-or-a-unix-socket). - The API tends to be REST, but for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `STDOUT`, `STDIN` and `STDERR`. @@ -24,11 +24,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -76,26 +76,20 @@ List containers } ] - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -103,16 +97,19 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json { "Hostname":"", + "Domainname": "", "User":"", "Memory":0, "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, @@ -129,15 +126,15 @@ Create a container "/tmp": {} }, "WorkingDir":"", - "DisableNetwork": false, + "NetworkDisabled": false, "ExposedPorts":{ "22/tcp": {} } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -145,25 +142,23 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: + +- **config** – the container's configuration + +Query Parameters:   - - **config** – the container's configuration - - Query Parameters: - -   - - - **name** – Assign the specified name to the container. Must +- **name** – Assign the specified name to the container. Mus match `/?[a-zA-Z0-9_-]+`. - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -172,11 +167,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -245,11 +240,11 @@ Return low-level information on the container `id` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -257,11 +252,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -286,17 +281,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Get container logs @@ -304,33 +297,32 @@ List processes running inside the container `id` Get stdout and stderr logs from the container ``id`` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1&tail=10 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   +- **follow** – 1/True/true or 0/False/false, return stream. Default false +- **stdout** – 1/True/true or 0/False/false, show stdout log. Default false +- **stderr** – 1/True/true or 0/False/false, show stderr log. Default false +- **timestamps** – 1/True/true or 0/False/false, print timestamps for every + log line. Default false +- **tail** – Output specified number of lines at the end of logs: `all` or + ``. Default all - - **follow** – 1/True/true or 0/False/false, return stream. Default false - - **stdout** – 1/True/true or 0/False/false, show stdout log. Default false - - **stderr** – 1/True/true or 0/False/false, show stderr log. Default false - - **timestamps** – 1/True/true or 0/False/false, print timestamps for - every log line. Default false - - **tail** – Output specified number of lines at the end of logs: `all` or ``. Default all +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -338,11 +330,11 @@ Get stdout and stderr logs from the container ``id`` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -362,11 +354,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -374,22 +366,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -397,7 +389,7 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json @@ -405,7 +397,7 @@ Start the container `id` { "Binds":["/tmp:/tmp"], "Links":["redis3:redis"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts":false, "Privileged":false, @@ -413,23 +405,23 @@ Start the container `id` "VolumesFrom": ["parent", "other:ro"] } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **204** – no error - - **304** – container already started - - **404** – no such container - - **500** – server error +- **204** – no error +- **304** – container already started +- **404** – no such container +- **500** – server error ### Stop a container @@ -437,26 +429,24 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **304** – container already stopped - - **404** – no such container - - **500** – server error +- **204** – no error +- **304** – container already stopped +- **404** – no such container +- **500** – server error ### Restart a container @@ -464,25 +454,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -490,24 +478,24 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters +Query Parameters - - **signal** - Signal to send to the container: integer or string like "SIGINT". - When not set, SIGKILL is assumed and the call will waits for the container to exit. +- **signal** - Signal to send to the container: integer or string like "SIGINT". + When not set, SIGKILL is assumed and the call will wait for the container to exit. - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Pause a container @@ -515,19 +503,19 @@ Kill the container `id` Pause the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/pause HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Unpause a container @@ -535,19 +523,19 @@ Pause the container `id` Unpause the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/unpause HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -555,38 +543,34 @@ Unpause the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   +- **logs** – 1/True/true or 0/False/false, return logs. Default false +- **stream** – 1/True/true or 0/False/false, return stream. Default false +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. + Default false +- **stdout** – 1/True/true or 0/False/false, if logs=true, return + stdout log, if stream=true, attach to stdout. Default false +- **stderr** – 1/True/true or 0/False/false, if logs=true, return + stderr log, if stream=true, attach to stderr. Default false - - **logs** – 1/True/true or 0/False/false, return logs. Default - false - - **stream** – 1/True/true or 0/False/false, return stream. - Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach - to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return - stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return - stderr log, if stream=true, attach to stderr. Default false +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -611,9 +595,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -630,7 +614,7 @@ Attach to the container `id` 2. chose stdout or stderr depending on the first byte 3. Extract the frame size from the last 4 byets 4. Read the extracted size and output it on the correct output - 5. Goto 1) + 5. Goto 1 ### Wait a container @@ -638,22 +622,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -661,29 +645,27 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - - **force** – 1/True/true or 0/False/false, Removes the container +- **force** – 1/True/true or 0/False/false, Removes the container even if it was running. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -691,7 +673,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -700,18 +682,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -723,7 +705,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -754,26 +736,22 @@ Copy files or folders of container `id` ] - Query Parameters: - -   - - - **all** – 1/True/true or 0/False/false, default false - - **filters** – a json encoded value of the filters (a map[string][]string) to process on the images list. - +Query Parameters: +- **all** – 1/True/true or 0/False/false, default false +- **filters** – a json encoded value of the filters (a map[string][string]) to process on the images list. ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pulling it from the registry or by importing it - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -787,26 +765,22 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error @@ -816,11 +790,11 @@ Create an image, either by pull it from the registry or by importing it Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -854,11 +828,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -866,11 +840,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -888,11 +862,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -900,11 +874,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -915,32 +889,27 @@ Push the image `name` on the registry ... If you wish to push an image on to a private registry, that image must already have been tagged - into a repository which references that registry host name and port. This repository name should + into a repository which references that registry host name and port. This repository name should then be used in the URL. This mirrors the flow of the CLI. - **Example request**: +**Example request**: - POST /images/registry.acme.com:5000/test/push HTTP/1.1 - + POST /images/registry.acme.com:5000/test/push HTTP/1.1 - Query Parameters: -   +Query Parameters: - - **tag** – the tag to associate with the image on the registry, optional +- **tag** – the tag to associate with the image on the registry, optional - Request Headers: +Request Headers: -   +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - - **X-Registry-Auth** – include a base64-encoded AuthConfig - object. +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -948,28 +917,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -977,11 +945,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -992,19 +960,17 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Query Parameters: +Query Parameters: -   +- **force** – 1/True/true or 0/False/false, default false +- **noprune** – 1/True/true or 0/False/false, default false - - **force** – 1/True/true or 0/False/false, default false - - **noprune** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -1016,11 +982,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1050,16 +1016,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -1069,13 +1033,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1092,29 +1056,24 @@ Build an image from Dockerfile via stdin which will be accessible in the build context (See the [*ADD build command*](/reference/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image - - **rm** - remove intermediate containers after a successful build (default behavior) - - **forcerm - always remove intermediate containers (includes rm) +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** - remove intermediate containers after a successful build (default behavior) +- **forcerm - always remove intermediate containers (includes rm) Request Headers: -   +- **Content-type** – should be set to `"application/tar"`. +- **X-Registry-Config** – base64-encoded ConfigFile objec - - **Content-type** – should be set to - `"application/tar"`. - - **X-Registry-Config** – base64-encoded ConfigFile object +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1122,7 +1081,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1134,15 +1093,15 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1150,11 +1109,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1176,10 +1135,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1187,11 +1146,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1203,10 +1162,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Ping the docker server @@ -1214,20 +1173,21 @@ Show the docker version information Ping the docker server - **Example request**: +**Example request**: GET /_ping HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain OK - Status Codes: +Status Codes: - - **200** - no error - - **500** - server error +- **200** - no error +- **500** - server error ### Create a new image from a container's changes @@ -1235,16 +1195,19 @@ Ping the docker server Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json { "Hostname":"", + "Domainname": "", "User":"", "Memory":0, "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, @@ -1260,54 +1223,58 @@ Create a new image from a container's changes "/tmp": {} }, "WorkingDir":"", - "DisableNetwork": false, + "NetworkDisabled": false, "ExposedPorts":{ "22/tcp": {} } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 Created + Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Json Parameters: +Json Parameters: +- **config** - the container's configuration +Query Parameters: - - **config** - the container's configuration +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith + <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Query Parameters: +Status Codes: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith - <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - - Status Codes: - - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` -Get events from docker, either in real time via streaming, or -via polling (using since) +Get container events from docker, either in real time via streaming, or via +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1317,17 +1284,15 @@ via polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling +- **until** – timestamp used for polling - - **since** – timestamp used for polling - - **until** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1336,21 +1301,23 @@ via polling (using since) Get a tarball containing all images and metadata for the repository specified by `name`. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/x-tar Binary data stream - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Load a tarball with a set of images and tags into docker @@ -1358,30 +1325,53 @@ specified by `name`. Load a set of images and tags into the docker repository. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** POST /images/load Tarball in body - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` # 3. Going further ## 3.1 Inside `docker run` -Here are the steps of `docker run`: +As an example, the `docker run` command line makes the following API calls: - Create the container -- If the status code is 404, it means the image doesn't exists: +- If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1402,6 +1392,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.14.md b/docs/sources/reference/api/docker_remote_api_v1.14.md index 9a9c36e54b..0c806bdd2e 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.14.md +++ b/docs/sources/reference/api/docker_remote_api_v1.14.md @@ -8,8 +8,8 @@ page_keywords: API, Docker, rcli, REST, documentation - The Remote API has replaced `rcli`. - The daemon listens on `unix:///var/run/docker.sock` but you can - [*Bind Docker to another host/port or a Unix socket*]( - /use/basics/#bind-docker). + [Bind Docker to another host/port or a Unix socket]( + /articles/basics/#bind-docker-to-another-hostport-or-a-unix-socket). - The API tends to be REST, but for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `STDOUT`, `STDIN` and `STDERR`. @@ -24,11 +24,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -76,26 +76,20 @@ List containers } ] - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -103,16 +97,19 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json { "Hostname":"", + "Domainname": "", "User":"", "Memory":0, "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, @@ -129,15 +126,16 @@ Create a container "/tmp": {} }, "WorkingDir":"", - "DisableNetwork": false, + "NetworkDisabled": false, "ExposedPorts":{ "22/tcp": {} - } + }, + "RestartPolicy": { "Name": "always" } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -145,25 +143,26 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **RestartPolicy** – The behavior to apply when the container exits. The + value is an object with a `Name` property of either `"always"` to + always restart or `"on-failure"` to restart only when the container + exit code is non-zero. If `on-failure` is used, `MaximumRetryCount` + controls the number of times to retry before giving up. + The default is not to restart. (optional) +- **config** – the container's configuration - - **config** – the container's configuration +Query Parameters: - Query Parameters: +- **name** – Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`. -   +Status Codes: - - **name** – Assign the specified name to the container. Must - match `/?[a-zA-Z0-9_-]+`. - - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -172,11 +171,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -247,11 +246,11 @@ Return low-level information on the container `id` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -259,11 +258,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -288,17 +287,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Get container logs @@ -306,33 +303,32 @@ List processes running inside the container `id` Get stdout and stderr logs from the container ``id`` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1&tail=10 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   +- **follow** – 1/True/true or 0/False/false, return stream. Default false +- **stdout** – 1/True/true or 0/False/false, show stdout log. Default false +- **stderr** – 1/True/true or 0/False/false, show stderr log. Default false +- **timestamps** – 1/True/true or 0/False/false, print timestamps for every + log line. Default false +- **tail** – Output specified number of lines at the end of logs: `all` or + ``. Default all - - **follow** – 1/True/true or 0/False/false, return stream. Default false - - **stdout** – 1/True/true or 0/False/false, show stdout log. Default false - - **stderr** – 1/True/true or 0/False/false, show stderr log. Default false - - **timestamps** – 1/True/true or 0/False/false, print timestamps for - every log line. Default false - - **tail** – Output specified number of lines at the end of logs: `all` or ``. Default all +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -340,11 +336,11 @@ Get stdout and stderr logs from the container ``id`` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -364,11 +360,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -376,22 +372,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -399,7 +395,7 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json @@ -407,33 +403,30 @@ Start the container `id` { "Binds":["/tmp:/tmp"], "Links":["redis3:redis"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts":false, "Privileged":false, "Dns": ["8.8.8.8"], "VolumesFrom": ["parent", "other:ro"], - "CapAdd: ["NET_ADMIN"], - "CapDrop: ["MKNOD"] + "CapAdd": ["NET_ADMIN"], + "CapDrop": ["MKNOD"] } - **Example response**: +**Example response**: HTTP/1.1 204 No Content - Content-Type: text/plain - Json Parameters: +Json Parameters: -   +- **hostConfig** – the container's host configuration (optional) - - **hostConfig** – the container's host configuration (optional) +Status Codes: - Status Codes: - - - **204** – no error - - **304** – container already started - - **404** – no such container - - **500** – server error +- **204** – no error +- **304** – container already started +- **404** – no such container +- **500** – server error ### Stop a container @@ -441,26 +434,24 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **304** – container already stopped - - **404** – no such container - - **500** – server error +- **204** – no error +- **304** – container already stopped +- **404** – no such container +- **500** – server error ### Restart a container @@ -468,25 +459,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -494,24 +483,24 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters +Query Parameters - - **signal** - Signal to send to the container: integer or string like "SIGINT". - When not set, SIGKILL is assumed and the call will waits for the container to exit. +- **signal** - Signal to send to the container: integer or string like "SIGINT". + When not set, SIGKILL is assumed and the call will wait for the container to exit. - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Pause a container @@ -519,19 +508,19 @@ Kill the container `id` Pause the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/pause HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Unpause a container @@ -539,19 +528,19 @@ Pause the container `id` Unpause the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/unpause HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -559,38 +548,34 @@ Unpause the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   +- **logs** – 1/True/true or 0/False/false, return logs. Default false +- **stream** – 1/True/true or 0/False/false, return stream. Default false +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. + Default false +- **stdout** – 1/True/true or 0/False/false, if logs=true, return + stdout log, if stream=true, attach to stdout. Default false +- **stderr** – 1/True/true or 0/False/false, if logs=true, return + stderr log, if stream=true, attach to stderr. Default false - - **logs** – 1/True/true or 0/False/false, return logs. Default - false - - **stream** – 1/True/true or 0/False/false, return stream. - Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach - to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return - stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return - stderr log, if stream=true, attach to stderr. Default false +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -615,9 +600,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -634,7 +619,7 @@ Attach to the container `id` 2. chose stdout or stderr depending on the first byte 3. Extract the frame size from the last 4 byets 4. Read the extracted size and output it on the correct output - 5. Goto 1) + 5. Goto 1 ### Wait a container @@ -642,22 +627,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -665,29 +650,27 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - - **force** - 1/True/true or 0/False/false, Kill then remove the container. +- **force** - 1/True/true or 0/False/false, Kill then remove the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -695,7 +678,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -704,18 +687,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -727,7 +710,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -758,26 +741,22 @@ Copy files or folders of container `id` ] - Query Parameters: - -   - - - **all** – 1/True/true or 0/False/false, default false - - **filters** – a json encoded value of the filters (a map[string][]string) to process on the images list. - +Query Parameters: +- **all** – 1/True/true or 0/False/false, default false +- **filters** – a json encoded value of the filters (a map[string][string]) to process on the images list. ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pulling it from the registry or by importing it - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -791,26 +770,22 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error @@ -820,11 +795,11 @@ Create an image, either by pull it from the registry or by importing it Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -858,11 +833,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -870,11 +845,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -892,11 +867,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -904,11 +879,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -919,32 +894,27 @@ Push the image `name` on the registry ... If you wish to push an image on to a private registry, that image must already have been tagged - into a repository which references that registry host name and port. This repository name should + into a repository which references that registry host name and port. This repository name should then be used in the URL. This mirrors the flow of the CLI. - **Example request**: +**Example request**: - POST /images/registry.acme.com:5000/test/push HTTP/1.1 - + POST /images/registry.acme.com:5000/test/push HTTP/1.1 - Query Parameters: -   +Query Parameters: - - **tag** – the tag to associate with the image on the registry, optional +- **tag** – the tag to associate with the image on the registry, optional - Request Headers: +Request Headers: -   +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - - **X-Registry-Auth** – include a base64-encoded AuthConfig - object. +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -952,28 +922,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -981,11 +950,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -996,19 +965,17 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Query Parameters: +Query Parameters: -   +- **force** – 1/True/true or 0/False/false, default false +- **noprune** – 1/True/true or 0/False/false, default false - - **force** – 1/True/true or 0/False/false, default false - - **noprune** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -1020,11 +987,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1054,16 +1021,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -1073,13 +1038,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1096,29 +1061,24 @@ Build an image from Dockerfile via stdin which will be accessible in the build context (See the [*ADD build command*](/reference/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image - - **rm** - remove intermediate containers after a successful build (default behavior) - - **forcerm - always remove intermediate containers (includes rm) +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** - remove intermediate containers after a successful build (default behavior) +- **forcerm - always remove intermediate containers (includes rm) Request Headers: -   +- **Content-type** – should be set to `"application/tar"`. +- **X-Registry-Config** – base64-encoded ConfigFile objec - - **Content-type** – should be set to - `"application/tar"`. - - **X-Registry-Config** – base64-encoded ConfigFile object +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1126,7 +1086,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1138,15 +1098,15 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1154,11 +1114,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1180,10 +1140,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1191,11 +1151,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1207,10 +1167,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Ping the docker server @@ -1218,20 +1178,21 @@ Show the docker version information Ping the docker server - **Example request**: +**Example request**: GET /_ping HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain OK - Status Codes: +Status Codes: - - **200** - no error - - **500** - server error +- **200** - no error +- **500** - server error ### Create a new image from a container's changes @@ -1239,16 +1200,19 @@ Ping the docker server Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json { "Hostname":"", + "Domainname": "", "User":"", "Memory":0, "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, @@ -1264,54 +1228,58 @@ Create a new image from a container's changes "/tmp": {} }, "WorkingDir":"", - "DisableNetwork": false, + "NetworkDisabled": false, "ExposedPorts":{ "22/tcp": {} } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 Created + Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Json Parameters: +Json Parameters: +- **config** - the container's configuration +Query Parameters: - - **config** - the container's configuration +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith + <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Query Parameters: +Status Codes: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith - <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - - Status Codes: - - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` -Get events from docker, either in real time via streaming, or -via polling (using since) +Get container events from docker, either in real time via streaming, or via +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1321,17 +1289,15 @@ via polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling +- **until** – timestamp used for polling - - **since** – timestamp used for polling - - **until** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1340,52 +1306,76 @@ via polling (using since) Get a tarball containing all images and metadata for the repository specified by `name`. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/x-tar Binary data stream - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Load a tarball with a set of images and tags into docker `POST /images/load` Load a set of images and tags into the docker repository. +See the [image tarball format](#image-tarball-format) for more details. - **Example request** +**Example request** POST /images/load Tarball in body - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` # 3. Going further ## 3.1 Inside `docker run` -Here are the steps of `docker run`: +As an example, the `docker run` command line makes the following API calls: - Create the container -- If the status code is 404, it means the image doesn't exists: +- If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1406,6 +1396,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.15.md b/docs/sources/reference/api/docker_remote_api_v1.15.md new file mode 100644 index 0000000000..e23fa0ff30 --- /dev/null +++ b/docs/sources/reference/api/docker_remote_api_v1.15.md @@ -0,0 +1,1573 @@ +page_title: Remote API v1.15 +page_description: API Documentation for Docker +page_keywords: API, Docker, rcli, REST, documentation + +# Docker Remote API v1.15 + +## 1. Brief introduction + + - The Remote API has replaced `rcli`. + - The daemon listens on `unix:///var/run/docker.sock` but you can + [Bind Docker to another host/port or a Unix socket]( + /articles/basics/#bind-docker-to-another-hostport-or-a-unix-socket). + - The API tends to be REST, but for some complex commands, like `attach` + or `pull`, the HTTP connection is hijacked to transport `STDOUT`, + `STDIN` and `STDERR`. + +# 2. Endpoints + +## 2.1 Containers + +### List containers + +`GET /containers/json` + +List containers + +**Example request**: + + GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Id": "8dfafdbc3a40", + "Image": "base:latest", + "Command": "echo 1", + "Created": 1367854155, + "Status": "Exit 0", + "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}], + "SizeRw":12288, + "SizeRootFs":0 + }, + { + "Id": "9cd87474be90", + "Image": "base:latest", + "Command": "echo 222222", + "Created": 1367854155, + "Status": "Exit 0", + "Ports":[], + "SizeRw":12288, + "SizeRootFs":0 + }, + { + "Id": "3176a2479c92", + "Image": "base:latest", + "Command": "echo 3333333333333333", + "Created": 1367854154, + "Status": "Exit 0", + "Ports":[], + "SizeRw":12288, + "SizeRootFs":0 + }, + { + "Id": "4cb07b47f9fb", + "Image": "base:latest", + "Command": "echo 444444444444444444444444444444444", + "Created": 1367854152, + "Status": "Exit 0", + "Ports":[], + "SizeRw":12288, + "SizeRootFs":0 + } + ] + +Query Parameters: + +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created + containers, include non-running ones. +- **since** – Show only containers created since Id, include + non-running ones. +- **before** – Show only containers created before Id, include + non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers + sizes + +Status Codes: + +- **200** – no error +- **400** – bad parameter +- **500** – server error + +### Create a container + +`POST /containers/create` + +Create a container + +**Example request**: + + POST /containers/create HTTP/1.1 + Content-Type: application/json + + { + "Hostname":"", + "Domainname": "", + "User":"", + "Memory":0, + "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", + "AttachStdin":false, + "AttachStdout":true, + "AttachStderr":true, + "PortSpecs":null, + "Tty":false, + "OpenStdin":false, + "StdinOnce":false, + "Env":null, + "Cmd":[ + "date" + ], + "Image":"base", + "Volumes":{ + "/tmp": {} + }, + "WorkingDir":"", + "NetworkDisabled": false, + "ExposedPorts":{ + "22/tcp": {} + }, + "RestartPolicy": { "Name": "always" } + } + +**Example response**: + + HTTP/1.1 201 Created + Content-Type: application/json + + { + "Id":"e90e34656806" + "Warnings":[] + } + +Json Parameters: + +- **RestartPolicy** – The behavior to apply when the container exits. The + value is an object with a `Name` property of either `"always"` to + always restart or `"on-failure"` to restart only when the container + exit code is non-zero. If `on-failure` is used, `MaximumRetryCount` + controls the number of times to retry before giving up. + The default is not to restart. (optional) +- **Volumes** – An object mapping mountpoint paths (strings) inside the + container to empty objects. +- **config** – the container's configuration + +Query Parameters: + +- **name** – Assign the specified name to the container. Must + match `/?[a-zA-Z0-9_-]+`. + +Status Codes: + +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error + +### Inspect a container + +`GET /containers/(id)/json` + +Return low-level information on the container `id` + + +**Example request**: + + GET /containers/4fa6e0f0c678/json HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2", + "Created": "2013-05-07T14:51:42.041847+02:00", + "Path": "date", + "Args": [], + "Config": { + "Hostname": "4fa6e0f0c678", + "User": "", + "Memory": 0, + "MemorySwap": 0, + "AttachStdin": false, + "AttachStdout": true, + "AttachStderr": true, + "PortSpecs": null, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": null, + "Cmd": [ + "date" + ], + "Dns": null, + "Image": "base", + "Volumes": {}, + "VolumesFrom": "", + "WorkingDir":"" + + }, + "State": { + "Running": false, + "Pid": 0, + "ExitCode": 0, + "StartedAt": "2013-05-07T14:51:42.087658+02:01360", + "Ghost": false + }, + "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "NetworkSettings": { + "IpAddress": "", + "IpPrefixLen": 0, + "Gateway": "", + "Bridge": "", + "PortMapping": null + }, + "SysInitPath": "/home/kitty/go/src/github.com/docker/docker/bin/docker", + "ResolvConfPath": "/etc/resolv.conf", + "Volumes": {}, + "HostConfig": { + "Binds": null, + "ContainerIDFile": "", + "LxcConf": [], + "Privileged": false, + "PortBindings": { + "80/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "49153" + } + ] + }, + "Links": ["/name:alias"], + "PublishAllPorts": false, + "CapAdd: ["NET_ADMIN"], + "CapDrop: ["MKNOD"] + } + } + +Status Codes: + +- **200** – no error +- **404** – no such container +- **500** – server error + +### List processes running inside a container + +`GET /containers/(id)/top` + +List processes running inside the container `id` + +**Example request**: + + GET /containers/4fa6e0f0c678/top HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Titles":[ + "USER", + "PID", + "%CPU", + "%MEM", + "VSZ", + "RSS", + "TTY", + "STAT", + "START", + "TIME", + "COMMAND" + ], + "Processes":[ + ["root","20147","0.0","0.1","18060","1864","pts/4","S","10:06","0:00","bash"], + ["root","20271","0.0","0.0","4312","352","pts/4","S+","10:07","0:00","sleep","10"] + ] + } + +Query Parameters: + +- **ps_args** – ps arguments to use (e.g., aux) + +Status Codes: + +- **200** – no error +- **404** – no such container +- **500** – server error + +### Get container logs + +`GET /containers/(id)/logs` + +Get stdout and stderr logs from the container ``id`` + +**Example request**: + + GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1×tamps=1&follow=1&tail=10 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + {{ STREAM }} + +Query Parameters: + +- **follow** – 1/True/true or 0/False/false, return stream. Default false +- **stdout** – 1/True/true or 0/False/false, show stdout log. Default false +- **stderr** – 1/True/true or 0/False/false, show stderr log. Default false +- **timestamps** – 1/True/true or 0/False/false, print timestamps for + every log line. Default false +- **tail** – Output specified number of lines at the end of logs: `all` or ``. Default all + +Status Codes: + +- **200** – no error +- **404** – no such container +- **500** – server error + +### Inspect changes on a container's filesystem + +`GET /containers/(id)/changes` + +Inspect changes on container `id`'s filesystem + +**Example request**: + + GET /containers/4fa6e0f0c678/changes HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Path":"/dev", + "Kind":0 + }, + { + "Path":"/dev/kmsg", + "Kind":1 + }, + { + "Path":"/test", + "Kind":1 + } + ] + +Status Codes: + +- **200** – no error +- **404** – no such container +- **500** – server error + +### Export a container + +`GET /containers/(id)/export` + +Export the contents of container `id` + +**Example request**: + + GET /containers/4fa6e0f0c678/export HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/octet-stream + + {{ TAR STREAM }} + +Status Codes: + +- **200** – no error +- **404** – no such container +- **500** – server error + +### Resize a container TTY + +`GET /containers/(id)/resize?h=&w=` + +Resize the TTY of container `id` + +**Example request**: + + GET /containers/4fa6e0f0c678/resize?h=40&w=80 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Length: 0 + Content-Type: text/plain; charset=utf-8 + +Status Codes: + +- **200** – no error +- **404** – No such container +- **500** – bad file descriptor + +### Start a container + +`POST /containers/(id)/start` + +Start the container `id` + +**Example request**: + + POST /containers/(id)/start HTTP/1.1 + Content-Type: application/json + + { + "Binds":["/tmp:/tmp"], + "Links":["redis3:redis"], + "LxcConf":{"lxc.utsname":"docker"}, + "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, + "PublishAllPorts":false, + "Privileged":false, + "Dns": ["8.8.8.8"], + "VolumesFrom": ["parent", "other:ro"], + "CapAdd": ["NET_ADMIN"], + "CapDrop": ["MKNOD"] + } + +**Example response**: + + HTTP/1.1 204 No Content + +Json Parameters: + +- **Binds** – A list of volume bindings for this container. Each volume + binding is a string of the form `container_path` (to create a new + volume for the container), `host_path:container_path` (to bind-mount + a host path into the container), or `host_path:container_path:ro` + (to make the bind-mount read-only inside the container). +- **hostConfig** – the container's host configuration (optional) + +Status Codes: + +- **204** – no error +- **304** – container already started +- **404** – no such container +- **500** – server error + +### Stop a container + +`POST /containers/(id)/stop` + +Stop the container `id` + +**Example request**: + + POST /containers/e90e34656806/stop?t=5 HTTP/1.1 + +**Example response**: + + HTTP/1.1 204 No Content + +Query Parameters: + +- **t** – number of seconds to wait before killing the container + +Status Codes: + +- **204** – no error +- **304** – container already stopped +- **404** – no such container +- **500** – server error + +### Restart a container + +`POST /containers/(id)/restart` + +Restart the container `id` + +**Example request**: + + POST /containers/e90e34656806/restart?t=5 HTTP/1.1 + +**Example response**: + + HTTP/1.1 204 No Content + +Query Parameters: + +- **t** – number of seconds to wait before killing the container + +Status Codes: + +- **204** – no error +- **404** – no such container +- **500** – server error + +### Kill a container + +`POST /containers/(id)/kill` + +Kill the container `id` + +**Example request**: + + POST /containers/e90e34656806/kill HTTP/1.1 + +**Example response**: + + HTTP/1.1 204 No Content + +Query Parameters + +- **signal** - Signal to send to the container: integer or string like "SIGINT". + When not set, SIGKILL is assumed and the call will waits for the container to exit. + +Status Codes: + +- **204** – no error +- **404** – no such container +- **500** – server error + +### Pause a container + +`POST /containers/(id)/pause` + +Pause the container `id` + +**Example request**: + + POST /containers/e90e34656806/pause HTTP/1.1 + +**Example response**: + + HTTP/1.1 204 No Content + +Status Codes: + +- **204** – no error +- **404** – no such container +- **500** – server error + +### Unpause a container + +`POST /containers/(id)/unpause` + +Unpause the container `id` + +**Example request**: + + POST /containers/e90e34656806/unpause HTTP/1.1 + +**Example response**: + + HTTP/1.1 204 No Content + +Status Codes: + +- **204** – no error +- **404** – no such container +- **500** – server error + +### Attach to a container + +`POST /containers/(id)/attach` + +Attach to the container `id` + +**Example request**: + + POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + {{ STREAM }} + +Query Parameters: + +- **logs** – 1/True/true or 0/False/false, return logs. Default false +- **stream** – 1/True/true or 0/False/false, return stream. + Default false +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach + to stdin. Default false +- **stdout** – 1/True/true or 0/False/false, if logs=true, return + stdout log, if stream=true, attach to stdout. Default false +- **stderr** – 1/True/true or 0/False/false, if logs=true, return + stderr log, if stream=true, attach to stderr. Default false + +Status Codes: + +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error + + **Stream details**: + + When using the TTY setting is enabled in + [`POST /containers/create` + ](../docker_remote_api_v1.9/#post--containers-create "POST /containers/create"), + the stream is the raw data from the process PTY and client's stdin. + When the TTY is disabled, then the stream is multiplexed to separate + stdout and stderr. + + The format is a **Header** and a **Payload** (frame). + + **HEADER** + + The header will contain the information on which stream write the + stream (stdout or stderr). It also contain the size of the + associated frame encoded on the last 4 bytes (uint32). + + It is encoded on the first 8 bytes like this: + + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + + `STREAM_TYPE` can be: + +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr + + `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of + the uint32 size encoded as big endian. + + **PAYLOAD** + + The payload is the raw stream. + + **IMPLEMENTATION** + + The simplest way to implement the Attach protocol is the following: + + 1. Read 8 bytes + 2. chose stdout or stderr depending on the first byte + 3. Extract the frame size from the last 4 byets + 4. Read the extracted size and output it on the correct output + 5. Goto 1 + +### Wait a container + +`POST /containers/(id)/wait` + +Block until container `id` stops, then returns the exit code + +**Example request**: + + POST /containers/16253994b7c4/wait HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + {"StatusCode":0} + +Status Codes: + +- **200** – no error +- **404** – no such container +- **500** – server error + +### Remove a container + +`DELETE /containers/(id)` + +Remove the container `id` from the filesystem + +**Example request**: + + DELETE /containers/16253994b7c4?v=1 HTTP/1.1 + +**Example response**: + + HTTP/1.1 204 No Content + +Query Parameters: + +- **v** – 1/True/true or 0/False/false, Remove the volumes + associated to the container. Default false +- **force** - 1/True/true or 0/False/false, Kill then remove the container. + Default false + +Status Codes: + +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error + +### Copy files or folders from a container + +`POST /containers/(id)/copy` + +Copy files or folders of container `id` + +**Example request**: + + POST /containers/4fa6e0f0c678/copy HTTP/1.1 + Content-Type: application/json + + { + "Resource":"test.txt" + } + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/x-tar + + {{ TAR STREAM }} + +Status Codes: + +- **200** – no error +- **404** – no such container +- **500** – server error + +## 2.2 Images + +### List Images + +`GET /images/json` + +**Example request**: + + GET /images/json?all=0 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "RepoTags": [ + "ubuntu:12.04", + "ubuntu:precise", + "ubuntu:latest" + ], + "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c", + "Created": 1365714795, + "Size": 131506275, + "VirtualSize": 131506275 + }, + { + "RepoTags": [ + "ubuntu:12.10", + "ubuntu:quantal" + ], + "ParentId": "27cf784147099545", + "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Created": 1364102658, + "Size": 24653, + "VirtualSize": 180116135 + } + ] + + +Query Parameters: + +- **all** – 1/True/true or 0/False/false, default false +- **filters** – a json encoded value of the filters (a map[string][]string) to process on the images list. + +### Create an image + +`POST /images/create` + +Create an image, either by pulling it from the registry or by importing it + +**Example request**: + + POST /images/create?fromImage=base HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Pulling..."} + {"status":"Pulling", "progress":"1 B/ 100 B", "progressDetail":{"current":1, "total":100}} + {"error":"Invalid..."} + ... + + When using this endpoint to pull an image from the registry, the + `X-Registry-Auth` header can be used to include + a base64-encoded AuthConfig object. + +Query Parameters: + +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from + + Request Headers: + +- **X-Registry-Auth** – base64-encoded AuthConfig object + +Status Codes: + +- **200** – no error +- **500** – server error + + + +### Inspect an image + +`GET /images/(name)/json` + +Return low-level information on the image `name` + +**Example request**: + + GET /images/base/json HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Created":"2013-03-23T22:24:18.818426-07:00", + "Container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0", + "ContainerConfig": + { + "Hostname":"", + "User":"", + "Memory":0, + "MemorySwap":0, + "AttachStdin":false, + "AttachStdout":false, + "AttachStderr":false, + "PortSpecs":null, + "Tty":true, + "OpenStdin":true, + "StdinOnce":false, + "Env":null, + "Cmd": ["/bin/bash"], + "Dns":null, + "Image":"base", + "Volumes":null, + "VolumesFrom":"", + "WorkingDir":"" + }, + "Id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Parent":"27cf784147099545", + "Size": 6824592 + } + +Status Codes: + +- **200** – no error +- **404** – no such image +- **500** – server error + +### Get the history of an image + +`GET /images/(name)/history` + +Return the history of the image `name` + +**Example request**: + + GET /images/base/history HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Id":"b750fe79269d", + "Created":1364102658, + "CreatedBy":"/bin/bash" + }, + { + "Id":"27cf78414709", + "Created":1364068391, + "CreatedBy":"" + } + ] + +Status Codes: + +- **200** – no error +- **404** – no such image +- **500** – server error + +### Push an image on the registry + +`POST /images/(name)/push` + +Push the image `name` on the registry + +**Example request**: + + POST /images/test/push HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Pushing..."} + {"status":"Pushing", "progress":"1/? (n/a)", "progressDetail":{"current":1}}} + {"error":"Invalid..."} + ... + + If you wish to push an image on to a private registry, that image must already have been tagged + into a repository which references that registry host name and port. This repository name should + then be used in the URL. This mirrors the flow of the CLI. + +**Example request**: + + POST /images/registry.acme.com:5000/test/push HTTP/1.1 + + +Query Parameters: + +- **tag** – the tag to associate with the image on the registry, optional + +Request Headers: + +- **X-Registry-Auth** – include a base64-encoded AuthConfig + object. + +Status Codes: + +- **200** – no error +- **404** – no such image +- **500** – server error + +### Tag an image into a repository + +`POST /images/(name)/tag` + +Tag the image `name` into a repository + +**Example request**: + + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 + +**Example response**: + + HTTP/1.1 201 OK + +Query Parameters: + +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name + +Status Codes: + +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error + +### Remove an image + +`DELETE /images/(name)` + +Remove the image `name` from the filesystem + +**Example request**: + + DELETE /images/test HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-type: application/json + + [ + {"Untagged":"3e2f21a89f"}, + {"Deleted":"3e2f21a89f"}, + {"Deleted":"53b4f83ac9"} + ] + +Query Parameters: + +- **force** – 1/True/true or 0/False/false, default false +- **noprune** – 1/True/true or 0/False/false, default false + +Status Codes: + +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error + +### Search images + +`GET /images/search` + +Search for an image on [Docker Hub](https://hub.docker.com). + +> **Note**: +> The response keys have changed from API v1.6 to reflect the JSON +> sent by the registry server to the docker daemon's request. + +**Example request**: + + GET /images/search?term=sshd HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "description": "", + "is_official": false, + "is_automated": false, + "name": "wma55/u1210sshd", + "star_count": 0 + }, + { + "description": "", + "is_official": false, + "is_automated": false, + "name": "jdswinbank/sshd", + "star_count": 0 + }, + { + "description": "", + "is_official": false, + "is_automated": false, + "name": "vgauthier/sshd", + "star_count": 0 + } + ... + ] + +Query Parameters: + +- **term** – term to search + +Status Codes: + +- **200** – no error +- **500** – server error + +## 2.3 Misc + +### Build an image from Dockerfile via stdin + +`POST /build` + +Build an image from Dockerfile via stdin + +**Example request**: + + POST /build HTTP/1.1 + + {{ TAR STREAM }} + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + {"stream":"Step 1..."} + {"stream":"..."} + {"error":"Error...", "errorDetail":{"code": 123, "message": "Error..."}} + + The stream must be a tar archive compressed with one of the + following algorithms: identity (no compression), gzip, bzip2, xz. + + The archive must include a file called `Dockerfile` + at its root. It may include any number of other files, + which will be accessible in the build context (See the [*ADD build + command*](/reference/builder/#dockerbuilder)). + +Query Parameters: + +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** - remove intermediate containers after a successful build (default behavior) +- **forcerm - always remove intermediate containers (includes rm) + + Request Headers: + +- **Content-type** – should be set to `"application/tar"`. +- **X-Registry-Config** – base64-encoded ConfigFile objec + +Status Codes: + +- **200** – no error +- **500** – server error + +### Check auth configuration + +`POST /auth` + +Get the default username and email + +**Example request**: + + POST /auth HTTP/1.1 + Content-Type: application/json + + { + "username":"hannibal", + "password:"xxxx", + "email":"hannibal@a-team.com", + "serveraddress":"https://index.docker.io/v1/" + } + +**Example response**: + + HTTP/1.1 200 OK + +Status Codes: + +- **200** – no error +- **204** – no error +- **500** – server error + +### Display system-wide information + +`GET /info` + +Display system-wide information + +**Example request**: + + GET /info HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Containers":11, + "Images":16, + "Driver":"btrfs", + "ExecutionDriver":"native-0.1", + "KernelVersion":"3.12.0-1-amd64" + "Debug":false, + "NFd": 11, + "NGoroutines":21, + "NEventsListener":0, + "InitPath":"/usr/bin/docker", + "IndexServerAddress":["https://index.docker.io/v1/"], + "MemoryLimit":true, + "SwapLimit":false, + "IPv4Forwarding":true + } + +Status Codes: + +- **200** – no error +- **500** – server error + +### Show the docker version information + +`GET /version` + +Show the docker version information + +**Example request**: + + GET /version HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "ApiVersion":"1.12", + "Version":"0.2.2", + "GitCommit":"5a2a5cc+CHANGES", + "GoVersion":"go1.0.3" + } + +Status Codes: + +- **200** – no error +- **500** – server error + +### Ping the docker server + +`GET /_ping` + +Ping the docker server + +**Example request**: + + GET /_ping HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: text/plain + + OK + +Status Codes: + +- **200** - no error +- **500** - server error + +### Create a new image from a container's changes + +`POST /commit` + +Create a new image from a container's changes + +**Example request**: + + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + Content-Type: application/json + + { + "Hostname":"", + "Domainname": "", + "User":"", + "Memory":0, + "MemorySwap":0, + "CpuShares": 512, + "Cpuset": "0,1", + "AttachStdin":false, + "AttachStdout":true, + "AttachStderr":true, + "PortSpecs":null, + "Tty":false, + "OpenStdin":false, + "StdinOnce":false, + "Env":null, + "Cmd":[ + "date" + ], + "Volumes":{ + "/tmp": {} + }, + "WorkingDir":"", + "NetworkDisabled": false, + "ExposedPorts":{ + "22/tcp": {} + } + } + +**Example response**: + + HTTP/1.1 201 Created + Content-Type: application/vnd.docker.raw-stream + + {"Id":"596069db4bf5"} + +Json Parameters: + +- **config** - the container's configuration + +Query Parameters: + +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith + <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") + +Status Codes: + +- **201** – no error +- **404** – no such container +- **500** – server error + +### Monitor Docker's events + +`GET /events` + +Get container events from docker, either in real time via streaming, or via +polling (using since). + +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: + + GET /events?since=1374067924 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924} + {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924} + {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} + {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} + +Query Parameters: + +- **since** – timestamp used for polling +- **until** – timestamp used for polling + +Status Codes: + +- **200** – no error +- **500** – server error + +### Get a tarball containing all images in a repository + +`GET /images/(name)/get` + +Get a tarball containing all images and metadata for the repository specified +by `name`. + +If `name` is a specific name and tag (e.g. ubuntu:latest), then only that image +(and its parents) are returned. If `name` is an image ID, similarly only tha +image (and its parents) are returned, but with the exclusion of the +'repositories' file in the tarball, as there were no image names referenced. + +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** + + GET /images/ubuntu/get + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/x-tar + + Binary data stream + +Status Codes: + +- **200** – no error +- **500** – server error + +### Get a tarball containing all images. + +`GET /images/get` + +Get a tarball containing all images and metadata for one or more repositories. + +For each value of the `names` parameter: if it is a specific name and tag (e.g. +ubuntu:latest), then only that image (and its parents) are returned; if it is +an image ID, similarly only that image (and its parents) are returned and there +would be no names referenced in the 'repositories' file for this image ID. + +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** + + GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/x-tar + + Binary data stream + +Status Codes: + +- **200** – no error +- **500** – server error + +### Load a tarball with a set of images and tags into docker + +`POST /images/load` + +Load a set of images and tags into the docker repository. +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** + + POST /images/load + + Tarball in body + +**Example response**: + + HTTP/1.1 200 OK + +Status Codes: + +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` + +### Exec Create + +`POST /containers/(id)/exec` + +Sets up an exec instance in a running container `id` + +**Example request**: + + POST /containers/e90e34656806/exec HTTP/1.1 + Content-Type: application/json + + { + "AttachStdin":false, + "AttachStdout":true, + "AttachStderr":true, + "Tty":false, + "Cmd":[ + "date" + ], + "Container":"e90e34656806", + } + +**Example response**: + + HTTP/1.1 201 OK + Content-Type: application/json + + { + "Id":"f90e34656806" + } + +Json Parameters: + +- **execConfig** ? exec configuration. + +Status Codes: + +- **201** – no error +- **404** – no such container + +### Exec Start + +`POST /exec/(id)/start` + +Starts a previously set up exec instance `id`. If `detach` is true, this API returns after +starting the `exec` command. Otherwise, this API sets up an interactive session with the `exec` command. + +**Example request**: + + POST /exec/e90e34656806/start HTTP/1.1 + Content-Type: application/json + + { + "Detach":false, + "Tty":false, + } + +**Example response**: + + HTTP/1.1 201 OK + Content-Type: application/json + + {{ STREAM }} + +Json Parameters: + +- **execConfig** ? exec configuration. + +Status Codes: + +- **201** – no error +- **404** – no such exec instance + + **Stream details**: + Similar to the stream behavior of `POST /container/(id)/attach` API + +### Exec Resize + +`POST /exec/(id)/resize` + +Resizes the tty session used by the exec command `id`. +This API is valid only if `tty` was specified as part of creating and starting the exec command. + +**Example request**: + + POST /exec/e90e34656806/resize HTTP/1.1 + Content-Type: plain/text + +**Example response**: + + HTTP/1.1 201 OK + Content-Type: plain/text + +Query Parameters: + +- **h** – height of tty session +- **w** – width + +Status Codes: + +- **201** – no error +- **404** – no such exec instance + +# 3. Going further + +## 3.1 Inside `docker run` + +As an example, the `docker run` command line makes the following API calls: + +- Create the container + +- If the status code is 404, it means the image doesn't exist: + - Try to pull it + - Then retry to create the container + +- Start the container + +- If you are not in detached mode: +- Attach to the container, using logs=1 (to have stdout and + stderr from the container's start) and stream=1 + +- If in detached mode or only stdin is attached: +- Display the container's id + +## 3.2 Hijacking + +In this version of the API, /attach, uses hijacking to transport stdin, +stdout and stderr on the same socket. This might change in the future. + +## 3.3 CORS Requests + +To enable cross origin requests to the remote api add the flag +"--api-enable-cors" when running docker in daemon mode. + + $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.2.md b/docs/sources/reference/api/docker_remote_api_v1.2.md index 2530fb90ae..8da486cf94 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.2.md +++ b/docs/sources/reference/api/docker_remote_api_v1.2.md @@ -22,11 +22,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -74,24 +74,22 @@ List containers } ] - Query Parameters: +Query Parameters: -   - - - **all** – 1/True/true or 0/False/false, Show all containers. +- **all** – 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default - - **limit** – Show `limit` last created +- **limit** – Show `limit` last created containers, include non-running ones. - - **since** – Show only containers created since Id, include +- **since** – Show only containers created since Id, include non-running ones. - - **before** – Show only containers created before Id, include +- **before** – Show only containers created before Id, include non-running ones. - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -99,7 +97,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -126,9 +124,9 @@ Create a container "VolumesFrom":"" } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -136,18 +134,16 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Status Codes: - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -156,11 +152,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -211,11 +207,11 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -223,11 +219,11 @@ Return low-level information on the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -247,11 +243,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -259,22 +255,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -282,19 +278,19 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/start HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -302,25 +298,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -328,25 +322,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -354,19 +346,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -374,38 +366,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Wait a container @@ -413,22 +403,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -436,27 +426,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -466,11 +454,11 @@ Remove the container `id` from the filesystem List images `format` could be json or viz (json default) - **Example request**: +**Example request**: GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -494,11 +482,11 @@ List images `format` could be json or viz (json default) } ] - **Example request**: +**Example request**: GET /images/viz HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: text/plain @@ -521,30 +509,28 @@ List images `format` could be json or viz (json default) base [style=invisible] } - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=ubuntu HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -554,20 +540,18 @@ Create an image, either by pull it from the registry or by importing it {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -575,11 +559,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -589,17 +573,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -607,11 +589,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/centos/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -644,11 +626,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -656,11 +638,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/fedora/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -679,11 +661,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -706,11 +688,11 @@ Push the image `name` on the registry {"error":"Invalid..."} ... - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -718,28 +700,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -747,11 +728,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -762,12 +743,12 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **204** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -775,11 +756,11 @@ Remove the image `name` from the filesystem Search for an image on [Docker Hub](https://hub.docker.com) - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -811,30 +792,29 @@ Search for an image on [Docker Hub](https://hub.docker.com) Build an image from Dockerfile - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **t** – repository name to be applied to the resulting image in +- **t** – repository name to be applied to the resulting image in case of success - - **remote** – resource to fetch, as URI +- **remote** – resource to fetch, as URI - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error {{ STREAM }} is the raw text output of the build command. It uses the HTTP Hijack method in order to stream. @@ -845,7 +825,7 @@ HTTP Hijack method in order to stream. Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -856,7 +836,7 @@ Get the default username and email "email":"hannibal@a-team.com" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -865,13 +845,13 @@ Get the default username and email "Status": "Login Succeeded" } - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **401** – unauthorized - - **403** – forbidden - - **500** – server error +- **200** – no error +- **204** – no error +- **401** – unauthorized +- **403** – forbidden +- **500** – server error ### Display system-wide information @@ -879,11 +859,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -898,10 +878,10 @@ Display system-wide information "SwapLimit":false } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -909,11 +889,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -924,10 +904,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -935,7 +915,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -945,29 +925,27 @@ Create a new image from a container's changes "PortSpecs":["22"] } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error # 3. Going further @@ -977,7 +955,7 @@ Here are the steps of `docker run` : - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -988,7 +966,7 @@ Here are the steps of `docker run` : stderr from the container's start) and stream=1 - If in detached mode or only stdin is attached: - - Display the container's + - Display the container's ## 3.2 Hijacking @@ -998,7 +976,7 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. > docker -d -H="[tcp://192.168.1.9:2375](tcp://192.168.1.9:2375)" > –api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.3.md b/docs/sources/reference/api/docker_remote_api_v1.3.md index ff6fcac944..087262d7c8 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.3.md +++ b/docs/sources/reference/api/docker_remote_api_v1.3.md @@ -22,11 +22,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -74,26 +74,22 @@ List containers } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -101,7 +97,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -128,9 +124,9 @@ Create a container "VolumesFrom":"" } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -138,18 +134,16 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Status Codes: - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -158,11 +152,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -213,11 +207,11 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -225,11 +219,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -249,11 +243,11 @@ List processes running inside the container `id` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -261,11 +255,11 @@ List processes running inside the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -285,11 +279,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -297,22 +291,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -320,7 +314,7 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json @@ -329,22 +323,22 @@ Start the container `id` "Binds":["/tmp:/tmp"] } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -352,25 +346,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -378,25 +370,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -404,19 +394,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -424,38 +414,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Wait a container @@ -463,22 +451,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -486,27 +474,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -516,11 +502,11 @@ Remove the container `id` from the filesystem List images `format` could be json or viz (json default) - **Example request**: +**Example request**: GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -544,11 +530,11 @@ List images `format` could be json or viz (json default) } ] - **Example request**: +**Example request**: GET /images/viz HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: text/plain @@ -571,30 +557,28 @@ List images `format` could be json or viz (json default) base [style=invisible] } - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=ubuntu HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -604,20 +588,18 @@ Create an image, either by pull it from the registry or by importing it {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -625,11 +607,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -639,17 +621,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -657,11 +637,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/centos/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -694,11 +674,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -706,11 +686,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/fedora/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -728,11 +708,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -755,11 +735,11 @@ Push the image `name` on the registry {"error":"Invalid..."} ... - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -767,28 +747,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -796,11 +775,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -811,12 +790,12 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -824,11 +803,11 @@ Remove the image `name` from the filesystem Search for an image on [Docker Hub](https://hub.docker.com) - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -860,13 +839,13 @@ Search for an image on [Docker Hub](https://hub.docker.com) Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK @@ -874,24 +853,22 @@ Build an image from Dockerfile via stdin The stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz. - The archive must include a file called Dockerfile at its root. It + The archive must include a file called Dockerfile at its root. I may include any number of other files, which will be accessible in the build context (See the ADD build command). The Content-type header should be set to "application/tar". - Query Parameters: +Query Parameters: -   +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -899,7 +876,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -910,15 +887,16 @@ Get the default username and email "email":"hannibal@a-team.com" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -926,11 +904,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -948,10 +926,10 @@ Display system-wide information "KernelVersion":"3.8.0-19-generic" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -959,11 +937,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -974,10 +952,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -985,7 +963,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -995,42 +973,48 @@ Create a new image from a container's changes "PortSpecs":["22"] } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, or via -polling (using since) +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1040,16 +1024,14 @@ polling (using since) {"status":"stop","id":"dfdf82bd3881","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error # 3. Going further @@ -1059,7 +1041,7 @@ Here are the steps of `docker run` : - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1080,6 +1062,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. > docker -d -H="192.168.1.9:2375" –api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.4.md b/docs/sources/reference/api/docker_remote_api_v1.4.md index 77d8e15504..bfd739f8df 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.4.md +++ b/docs/sources/reference/api/docker_remote_api_v1.4.md @@ -22,11 +22,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -74,26 +74,22 @@ List containers } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -101,7 +97,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -131,9 +127,9 @@ Create a container } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -141,18 +137,16 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Status Codes: - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -161,11 +155,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -218,12 +212,12 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **409** – conflict between containers and images - - **500** – server error +- **200** – no error +- **404** – no such container +- **409** – conflict between containers and images +- **500** – server error ### List processes running inside a container @@ -231,11 +225,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -260,17 +254,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -278,11 +270,11 @@ List processes running inside the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -302,11 +294,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -314,22 +306,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -337,7 +329,7 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json @@ -347,22 +339,22 @@ Start the container `id` "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}] } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -370,25 +362,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -396,25 +386,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -422,19 +410,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -442,38 +430,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Wait a container @@ -481,22 +467,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -504,27 +490,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -532,7 +516,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -541,18 +525,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -562,11 +546,11 @@ Copy files or folders of container `id` List images `format` could be json or viz (json default) - **Example request**: +**Example request**: GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -590,11 +574,11 @@ List images `format` could be json or viz (json default) } ] - **Example request**: +**Example request**: GET /images/viz HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: text/plain @@ -617,30 +601,28 @@ List images `format` could be json or viz (json default) base [style=invisible] } - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=ubuntu HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -650,20 +632,18 @@ Create an image, either by pull it from the registry or by importing it {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -671,11 +651,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -685,17 +665,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -703,11 +681,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/centos/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -741,12 +719,12 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict between containers and images - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict between containers and images +- **500** – server error ### Get the history of an image @@ -754,11 +732,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/fedora/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -776,11 +754,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -788,12 +766,12 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 {{ authConfig }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -801,9 +779,9 @@ Push the image `name` on the registry {"status":"Pushing..."} {"status":"Pushing", "progress":"1/? (n/a)"} {"error":"Invalid..."} ... - Status Codes: +Status Codes: - - **200** – no error :statuscode 404: no such image :statuscode +- **200** – no error :statuscode 404: no such image :statuscode 500: server error ### Tag an image into a repository @@ -812,28 +790,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -841,11 +818,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -856,12 +833,12 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -869,11 +846,11 @@ Remove the image `name` from the filesystem Search for an image on [Docker Hub](https://hub.docker.com) - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -905,13 +882,13 @@ Search for an image on [Docker Hub](https://hub.docker.com) Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK @@ -919,25 +896,23 @@ Build an image from Dockerfile via stdin The stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz. - The archive must include a file called Dockerfile at its root. It + The archive must include a file called Dockerfile at its root. I may include any number of other files, which will be accessible in the build context (See the ADD build command). The Content-type header should be set to "application/tar". - Query Parameters: +Query Parameters: -   +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -945,7 +920,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -957,15 +932,16 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -973,11 +949,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -993,10 +969,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1004,26 +980,26 @@ Display system-wide information Show the docker version information - - **Example request**: - + +**Example request**: + GET /version HTTP/1.1 - - **Example response**: - + +**Example response**: + HTTP/1.1 200 OK Content-Type: application/json - + { "Version":"0.2.2", "GitCommit":"5a2a5cc+CHANGES", "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -1031,7 +1007,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -1041,42 +1017,48 @@ Create a new image from a container's changes "PortSpecs":["22"] } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, or via -polling (using since) +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1086,16 +1068,14 @@ polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error # 3. Going further @@ -1105,7 +1085,7 @@ Here are the steps of `docker run` : - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1126,6 +1106,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.5.md b/docs/sources/reference/api/docker_remote_api_v1.5.md index abf6e3397a..1c6b15df70 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.5.md +++ b/docs/sources/reference/api/docker_remote_api_v1.5.md @@ -22,11 +22,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -74,26 +74,22 @@ List containers } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -101,7 +97,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -130,9 +126,9 @@ Create a container "WorkingDir":"" } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -140,18 +136,16 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Status Codes: - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -160,11 +154,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -216,11 +210,11 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -228,11 +222,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -257,17 +251,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -275,11 +267,11 @@ List processes running inside the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -299,11 +291,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -311,22 +303,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -334,7 +326,7 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json @@ -344,22 +336,22 @@ Start the container `id` "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}] } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -367,25 +359,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -393,25 +383,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -419,19 +407,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -439,38 +427,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Wait a container @@ -478,22 +464,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -501,27 +487,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -529,7 +513,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -538,18 +522,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -559,11 +543,11 @@ Copy files or folders of container `id` List images `format` could be json or viz (json default) - **Example request**: +**Example request**: GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -587,11 +571,11 @@ List images `format` could be json or viz (json default) } ] - **Example request**: +**Example request**: GET /images/viz HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: text/plain @@ -614,30 +598,28 @@ List images `format` could be json or viz (json default) base [style=invisible] } - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=ubuntu HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -651,20 +633,18 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -672,11 +652,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -686,17 +666,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -704,11 +682,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/centos/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -742,11 +720,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -754,11 +732,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/fedora/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -776,11 +754,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -788,11 +766,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -805,11 +783,11 @@ Push the image `name` on the registry The `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -817,28 +795,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -846,11 +823,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -861,12 +838,12 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -874,11 +851,11 @@ Remove the image `name` from the filesystem Search for an image on [Docker Hub](https://hub.docker.com) - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -898,16 +875,14 @@ Search for an image on [Docker Hub](https://hub.docker.com) } ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -917,13 +892,13 @@ Search for an image on [Docker Hub](https://hub.docker.com) Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK @@ -931,26 +906,24 @@ Build an image from Dockerfile via stdin The stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz. - The archive must include a file called Dockerfile at its root. It + The archive must include a file called Dockerfile at its root. I may include any number of other files, which will be accessible in the build context (See the ADD build command). The Content-type header should be set to "application/tar". - Query Parameters: +Query Parameters: -   +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** – remove intermediate containers after a successful build - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image - - **rm** – remove intermediate containers after a successful build +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -958,7 +931,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -970,15 +943,16 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -986,11 +960,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1006,10 +980,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1017,11 +991,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1032,10 +1006,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -1043,7 +1017,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -1053,42 +1027,48 @@ Create a new image from a container's changes "PortSpecs":["22"] } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, or via -polling (using since) +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1098,16 +1078,14 @@ polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error # 3. Going further @@ -1116,7 +1094,7 @@ polling (using since) Here are the steps of `docker run`: - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: Try to pull it - Then retry to create the container - Start the container - If you are not in detached mode: @@ -1133,6 +1111,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.6.md b/docs/sources/reference/api/docker_remote_api_v1.6.md index 11dd45229c..39d87f38f6 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.6.md +++ b/docs/sources/reference/api/docker_remote_api_v1.6.md @@ -23,11 +23,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -75,26 +75,22 @@ List containers } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -102,7 +98,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -130,9 +126,9 @@ Create a container "WorkingDir":"" } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -140,27 +136,25 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: + +- **config** – the container's configuration + +Query Parameters:   - - **config** – the container's configuration +- **name** – container name to use - Query Parameters: +Status Codes: -   - - - **name** – container name to use - - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error **More Complex Example request, in 2 steps.** **First, use create to - expose a Private Port, which can be bound back to a Public Port at + expose a Private Port, which can be bound back to a Public Port a startup**: POST /containers/create HTTP/1.1 @@ -174,7 +168,7 @@ Create a container "ExposedPorts":{"22/tcp":{}} } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/json @@ -184,7 +178,7 @@ Create a container "Warnings":[] } - **Second, start (using the ID returned above) the image we just + **Second, start (using the ID returned above) the image we jus created, mapping the ssh port 22 to something on the host**: POST /containers/e90e34656806/start HTTP/1.1 @@ -194,9 +188,9 @@ Create a container "PortBindings": { "22/tcp": [{ "HostPort": "11022" }]} } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain; charset=utf-8 Content-Length: 0 @@ -209,11 +203,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -266,11 +260,11 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -278,11 +272,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -307,17 +301,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -325,11 +317,11 @@ List processes running inside the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -349,11 +341,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -361,22 +353,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -384,14 +376,14 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "ContainerIDFile": "", "Privileged": false, "PortBindings": {"22/tcp": [{HostIp:"", HostPort:""}]}, @@ -399,22 +391,22 @@ Start the container `id` "PublishAllPorts": false } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **hostConfig** – the container's host configuration (optional) +- **hostConfig** – the container's host configuration (optional) - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -422,25 +414,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -448,25 +438,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -474,27 +462,27 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters:   - - **signal** – Signal to send to the container (integer). When not +- **signal** – Signal to send to the container (integer). When no set, SIGKILL is assumed and the call will waits for the container to exit. - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -502,38 +490,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -558,9 +544,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -585,22 +571,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -608,27 +594,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -636,7 +620,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -645,18 +629,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -666,11 +650,11 @@ Copy files or folders of container `id` List images `format` could be json or viz (json default) - **Example request**: +**Example request**: GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -694,11 +678,11 @@ List images `format` could be json or viz (json default) } ] - **Example request**: +**Example request**: GET /images/viz HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: text/plain @@ -721,30 +705,28 @@ List images `format` could be json or viz (json default) base [style=invisible] } - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by defaul - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create an image `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -758,20 +740,18 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -779,11 +759,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -793,17 +773,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -811,11 +789,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -849,11 +827,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -861,11 +839,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -883,11 +861,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -895,11 +873,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -910,9 +888,9 @@ Push the image `name` on the registry > The `X-Registry-Auth` header can be used to > include a base64-encoded AuthConfig object. - Status Codes: +Status Codes: - - **200** – no error :statuscode 404: no such image :statuscode +- **200** – no error :statuscode 404: no such image :statuscode 500: server error ### Tag an image into a repository @@ -921,28 +899,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -950,11 +927,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -965,12 +942,12 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -978,11 +955,11 @@ Remove the image `name` from the filesystem Search for an image on [Docker Hub](https://hub.docker.com) - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1014,13 +991,13 @@ Search for an image on [Docker Hub](https://hub.docker.com) Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK @@ -1028,25 +1005,23 @@ Build an image from Dockerfile via stdin The stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz. - The archive must include a file called Dockerfile at its root. It + The archive must include a file called Dockerfile at its root. I may include any number of other files, which will be accessible in the build context (See the ADD build command). The Content-type header should be set to "application/tar". - Query Parameters: +Query Parameters: -   +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1054,7 +1029,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1066,15 +1041,16 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1082,11 +1058,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1102,10 +1078,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1113,11 +1089,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1128,10 +1104,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -1139,7 +1115,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -1149,42 +1125,48 @@ Create a new image from a container's changes "ExposedPorts":{"22/tcp":{}} } - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, or via -polling (using since) +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1194,16 +1176,14 @@ polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error # 3. Going further @@ -1213,7 +1193,7 @@ Here are the steps of `docker run` : - Create the container -- If the status code is 404, it means the image doesn't exists: +- If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1234,6 +1214,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.7.md b/docs/sources/reference/api/docker_remote_api_v1.7.md index 10ff841799..6e5387a80e 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.7.md +++ b/docs/sources/reference/api/docker_remote_api_v1.7.md @@ -23,11 +23,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -75,26 +75,20 @@ List containers } ] - Query Parameters: +Query Parameters: -   +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +Status Codes: - Status Codes: - - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -102,7 +96,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -135,9 +129,9 @@ Create a container } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -145,18 +139,16 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters: -   +- **config** – the container's configuration - - **config** – the container's configuration +Status Codes: - Status Codes: - - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -165,11 +157,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -222,11 +214,11 @@ Return low-level information on the container `id` "Volumes": {} } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -234,11 +226,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -263,17 +255,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -281,11 +271,11 @@ List processes running inside the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -305,11 +295,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -317,22 +307,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -340,14 +330,14 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "Privileged":false, "PublishAllPorts":false @@ -356,22 +346,20 @@ Start the container `id` Binds need to reference Volumes that were defined during container creation. - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters: -   +- **hostConfig** – the container's host configuration (optional) - - **hostConfig** – the container's host configuration (optional) +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -379,25 +367,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -405,25 +391,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -431,19 +415,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -451,38 +435,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -507,9 +489,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -534,22 +516,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -557,27 +539,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -585,7 +565,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -594,18 +574,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -617,7 +597,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -651,13 +631,13 @@ Copy files or folders of container `id` `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -671,26 +651,22 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -698,11 +674,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -712,17 +688,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -730,11 +704,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -768,11 +742,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -780,11 +754,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -802,11 +776,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -814,11 +788,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -832,14 +806,14 @@ Push the image `name` on the registry   - - **X-Registry-Auth** – include a base64-encoded AuthConfig +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -847,28 +821,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -876,11 +849,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -891,12 +864,12 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -908,11 +881,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -942,16 +915,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -961,13 +932,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -982,26 +953,24 @@ Build an image from Dockerfile via stdin which will be accessible in the build context (See the [*ADD build command*](/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image Request Headers:   - - **Content-type** – should be set to +- **Content-type** – should be set to `"application/tar"`. - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1009,7 +978,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1021,15 +990,16 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1037,11 +1007,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1057,10 +1027,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1068,11 +1038,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1083,10 +1053,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -1094,48 +1064,54 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - - **run** – config automatically applied when the image is run. +- **run** – config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, or via -polling (using since) +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1145,16 +1121,14 @@ polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1163,11 +1137,11 @@ polling (using since) Get a tarball containing all images and metadata for the repository specified by `name`. - **Example request** +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: .. sourcecode:: http @@ -1184,7 +1158,7 @@ specified by `name`. Load a set of images and tags into the docker repository. - **Example request** +**Example request** POST /images/load @@ -1207,7 +1181,7 @@ Here are the steps of `docker run` : - Create the container -- If the status code is 404, it means the image doesn't exists: +- If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1228,6 +1202,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.8.md b/docs/sources/reference/api/docker_remote_api_v1.8.md index b8bc0087c8..36c92a4aee 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.8.md +++ b/docs/sources/reference/api/docker_remote_api_v1.8.md @@ -23,11 +23,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -75,26 +75,22 @@ List containers } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -102,7 +98,7 @@ List containers Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -136,9 +132,9 @@ Create a container } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -146,38 +142,38 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters:   - - **Hostname** – Container host name - - **User** – Username or UID - - **Memory** – Memory Limit in bytes - - **CpuShares** – CPU shares (relative weight) - - **AttachStdin** – 1/True/true or 0/False/false, attach to +- **Hostname** – Container host name +- **User** – Username or UID +- **Memory** – Memory Limit in bytes +- **CpuShares** – CPU shares (relative weight) +- **AttachStdin** – 1/True/true or 0/False/false, attach to standard input. Default false - - **AttachStdout** – 1/True/true or 0/False/false, attach to +- **AttachStdout** – 1/True/true or 0/False/false, attach to standard output. Default false - - **AttachStderr** – 1/True/true or 0/False/false, attach to +- **AttachStderr** – 1/True/true or 0/False/false, attach to standard error. Default false - - **Tty** – 1/True/true or 0/False/false, allocate a pseudo-tty. +- **Tty** – 1/True/true or 0/False/false, allocate a pseudo-tty. Default false - - **OpenStdin** – 1/True/true or 0/False/false, keep stdin open +- **OpenStdin** – 1/True/true or 0/False/false, keep stdin open even if not attached. Default false - Query Parameters: +Query Parameters:   - - **name** – Assign the specified name to the container. Must +- **name** – Assign the specified name to the container. Mus match `/?[a-zA-Z0-9_-]+`. - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -185,11 +181,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -258,11 +254,11 @@ Return low-level information on the container `id` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -270,11 +266,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -299,17 +295,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -317,11 +311,11 @@ List processes running inside the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -341,11 +335,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -353,22 +347,22 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Start a container @@ -376,44 +370,44 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts":false, "Privileged":false } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **Binds** – Create a bind mount to a directory or file with +- **Binds** – Create a bind mount to a directory or file with [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume. - - **LxcConf** – Map of custom lxc options - - **PortBindings** – Expose ports from the container, optionally +- **LxcConf** – Map of custom lxc options +- **PortBindings** – Expose ports from the container, optionally publishing them via the HostPort flag - - **PublishAllPorts** – 1/True/true or 0/False/false, publish all +- **PublishAllPorts** – 1/True/true or 0/False/false, publish all exposed ports to the host interfaces. Default false - - **Privileged** – 1/True/true or 0/False/false, give extended +- **Privileged** – 1/True/true or 0/False/false, give extended privileges to this container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -421,25 +415,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -447,25 +439,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -473,19 +463,19 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -493,38 +483,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -549,9 +537,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -576,22 +564,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -599,27 +587,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -627,7 +613,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -636,18 +622,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -659,7 +645,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -693,13 +679,13 @@ Copy files or folders of container `id` `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -713,26 +699,22 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -740,11 +722,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -754,17 +736,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -772,11 +752,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -810,11 +790,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -822,11 +802,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -844,11 +824,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -856,11 +836,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -874,14 +854,14 @@ Push the image `name` on the registry   - - **X-Registry-Auth** – include a base64-encoded AuthConfig +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -889,28 +869,27 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image @@ -918,11 +897,11 @@ Tag the image `name` into a repository Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -933,12 +912,12 @@ Remove the image `name` from the filesystem {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -950,11 +929,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -984,16 +963,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -1003,13 +980,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile via stdin - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1026,27 +1003,25 @@ Build an image from Dockerfile via stdin which will be accessible in the build context (See the [*ADD build command*](/reference/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image Request Headers:   - - **Content-type** – should be set to +- **Content-type** – should be set to `"application/tar"`. - - **X-Registry-Auth** – base64-encoded AuthConfig object +- **X-Registry-Auth** – base64-encoded AuthConfig objec - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1054,7 +1029,7 @@ Build an image from Dockerfile via stdin Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1066,15 +1041,16 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1082,11 +1058,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1102,10 +1078,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1113,11 +1089,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1128,10 +1104,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -1139,48 +1115,54 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Query Parameters: +Query Parameters: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - - **run** – config automatically applied when the image is run. +- **run** – config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, -or via polling (using since) +or via polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1190,16 +1172,14 @@ or via polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1207,22 +1187,23 @@ or via polling (using since) Get a tarball containing all images and metadata for the repository specified by `name`. +See the [image tarball format](#image-tarball-format) for more details. - **Example request** +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/x-tar Binary data stream - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Load a tarball with a set of images and tags into docker @@ -1230,20 +1211,43 @@ specified by `name`. Load a set of images and tags into the docker repository. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** POST /images/load Tarball in body - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` # 3. Going further @@ -1253,7 +1257,7 @@ Here are the steps of `docker run`: - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: - Try to pull it - Then retry to create the container @@ -1274,6 +1278,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/docker_remote_api_v1.9.md b/docs/sources/reference/api/docker_remote_api_v1.9.md index 38f4ca8f54..7cac380109 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.9.md +++ b/docs/sources/reference/api/docker_remote_api_v1.9.md @@ -23,11 +23,11 @@ page_keywords: API, Docker, rcli, REST, documentation List containers. - **Example request**: +**Example request**: GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -75,26 +75,22 @@ List containers. } ] - Query Parameters: +Query Parameters:   - - **all** – 1/True/true or 0/False/false, Show all containers. - Only running containers are shown by default - - **limit** – Show `limit` last created - containers, include non-running ones. - - **since** – Show only containers created since Id, include - non-running ones. - - **before** – Show only containers created before Id, include - non-running ones. - - **size** – 1/True/true or 0/False/false, Show the containers - sizes +- **all** – 1/True/true or 0/False/false, Show all containers. + Only running containers are shown by default (i.e., this defaults to false) +- **limit** – Show `limit` last created containers, include non-running ones. +- **since** – Show only containers created since Id, include non-running ones. +- **before** – Show only containers created before Id, include non-running ones. +- **size** – 1/True/true or 0/False/false, Show the containers sizes - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **500** – server error ### Create a container @@ -102,7 +98,7 @@ List containers. Create a container - **Example request**: +**Example request**: POST /containers/create HTTP/1.1 Content-Type: application/json @@ -136,9 +132,9 @@ Create a container } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK + HTTP/1.1 201 Created Content-Type: application/json { @@ -146,38 +142,38 @@ Create a container "Warnings":[] } - Json Parameters: +Json Parameters:   - - **Hostname** – Container host name - - **User** – Username or UID - - **Memory** – Memory Limit in bytes - - **CpuShares** – CPU shares (relative weight) - - **AttachStdin** – 1/True/true or 0/False/false, attach to +- **Hostname** – Container host name +- **User** – Username or UID +- **Memory** – Memory Limit in bytes +- **CpuShares** – CPU shares (relative weight) +- **AttachStdin** – 1/True/true or 0/False/false, attach to standard input. Default false - - **AttachStdout** – 1/True/true or 0/False/false, attach to +- **AttachStdout** – 1/True/true or 0/False/false, attach to standard output. Default false - - **AttachStderr** – 1/True/true or 0/False/false, attach to +- **AttachStderr** – 1/True/true or 0/False/false, attach to standard error. Default false - - **Tty** – 1/True/true or 0/False/false, allocate a pseudo-tty. +- **Tty** – 1/True/true or 0/False/false, allocate a pseudo-tty. Default false - - **OpenStdin** – 1/True/true or 0/False/false, keep stdin open +- **OpenStdin** – 1/True/true or 0/False/false, keep stdin open even if not attached. Default false - Query Parameters: +Query Parameters:   - - **name** – Assign the specified name to the container. Must +- **name** – Assign the specified name to the container. Mus match `/?[a-zA-Z0-9_-]+`. - Status Codes: +Status Codes: - - **201** – no error - - **404** – no such container - - **406** – impossible to attach (container not running) - - **500** – server error +- **201** – no error +- **404** – no such container +- **406** – impossible to attach (container not running) +- **500** – server error ### Inspect a container @@ -185,11 +181,11 @@ Create a container Return low-level information on the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -258,11 +254,11 @@ Return low-level information on the container `id` } } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### List processes running inside a container @@ -270,11 +266,11 @@ Return low-level information on the container `id` List processes running inside the container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/top HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -299,17 +295,15 @@ List processes running inside the container `id` ] } - Query Parameters: +Query Parameters: -   +- **ps_args** – ps arguments to use (e.g., aux) - - **ps_args** – ps arguments to use (e.g., aux) +Status Codes: - Status Codes: - - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Inspect changes on a container's filesystem @@ -317,11 +311,11 @@ List processes running inside the container `id` Inspect changes on container `id`'s filesystem - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/changes HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -341,11 +335,11 @@ Inspect changes on container `id`'s filesystem } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Export a container @@ -353,22 +347,44 @@ Inspect changes on container `id`'s filesystem Export the contents of container `id` - **Example request**: +**Example request**: GET /containers/4fa6e0f0c678/export HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error + +### Resize a container TTY + +`GET /containers/(id)/resize?h=&w=` + +Resize the TTY of container `id` + +**Example request**: + + GET /containers/4fa6e0f0c678/resize?h=40&w=80 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Length: 0 + Content-Type: text/plain; charset=utf-8 + +Status Codes: + +- **200** – no error +- **404** – No such container +- **500** – bad file descriptor ### Start a container @@ -376,44 +392,44 @@ Export the contents of container `id` Start the container `id` - **Example request**: +**Example request**: POST /containers/(id)/start HTTP/1.1 Content-Type: application/json { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"}, + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts":false, "Privileged":false } - **Example response**: +**Example response**: - HTTP/1.1 204 No Content + HTTP/1.1 204 No Conten Content-Type: text/plain - Json Parameters: +Json Parameters:   - - **Binds** – Create a bind mount to a directory or file with +- **Binds** – Create a bind mount to a directory or file with [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume. - - **LxcConf** – Map of custom lxc options - - **PortBindings** – Expose ports from the container, optionally +- **LxcConf** – Map of custom lxc options +- **PortBindings** – Expose ports from the container, optionally publishing them via the HostPort flag - - **PublishAllPorts** – 1/True/true or 0/False/false, publish all +- **PublishAllPorts** – 1/True/true or 0/False/false, publish all exposed ports to the host interfaces. Default false - - **Privileged** – 1/True/true or 0/False/false, give extended +- **Privileged** – 1/True/true or 0/False/false, give extended privileges to this container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Stop a container @@ -421,25 +437,23 @@ Start the container `id` Stop the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/stop?t=5 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 204 OK - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Restart a container @@ -447,25 +461,23 @@ Stop the container `id` Restart the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/restart?t=5 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   +- **t** – number of seconds to wait before killing the container - - **t** – number of seconds to wait before killing the container +Status Codes: - Status Codes: - - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Kill a container @@ -473,24 +485,24 @@ Restart the container `id` Kill the container `id` - **Example request**: +**Example request**: POST /containers/e90e34656806/kill HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters +Query Parameters - - **signal** - Signal to send to the container: integer or string like "SIGINT". - When not set, SIGKILL is assumed and the call will waits for the container to exit. +- **signal** - Signal to send to the container: integer or string like "SIGINT". + When not set, SIGKILL is assumed and the call will wait for the container to exit. - Status Codes: +Status Codes: - - **204** – no error - - **404** – no such container - - **500** – server error +- **204** – no error +- **404** – no such container +- **500** – server error ### Attach to a container @@ -498,38 +510,36 @@ Kill the container `id` Attach to the container `id` - **Example request**: +**Example request**: POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }} - Query Parameters: +Query Parameters: -   - - - **logs** – 1/True/true or 0/False/false, return logs. Default +- **logs** – 1/True/true or 0/False/false, return logs. Defaul false - - **stream** – 1/True/true or 0/False/false, return stream. +- **stream** – 1/True/true or 0/False/false, return stream. Default false - - **stdin** – 1/True/true or 0/False/false, if stream=true, attach +- **stdin** – 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false - - **stdout** – 1/True/true or 0/False/false, if logs=true, return +- **stdout** – 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false - - **stderr** – 1/True/true or 0/False/false, if logs=true, return +- **stderr** – 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false - Status Codes: +Status Codes: - - **200** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **200** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error **Stream details**: @@ -553,9 +563,9 @@ Attach to the container `id` `STREAM_TYPE` can be: - - 0: stdin (will be written on stdout) - - 1: stdout - - 2: stderr +- 0: stdin (will be written on stdout) +- 1: stdou +- 2: stderr `SIZE1, SIZE2, SIZE3, SIZE4` are the 4 bytes of the uint32 size encoded as big endian. @@ -580,22 +590,22 @@ Attach to the container `id` Block until container `id` stops, then returns the exit code - **Example request**: +**Example request**: POST /containers/16253994b7c4/wait HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json {"StatusCode":0} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ### Remove a container @@ -603,27 +613,25 @@ Block until container `id` stops, then returns the exit code Remove the container `id` from the filesystem - **Example request**: +**Example request**: DELETE /containers/16253994b7c4?v=1 HTTP/1.1 - **Example response**: +**Example response**: - HTTP/1.1 204 OK + HTTP/1.1 204 No Content - Query Parameters: +Query Parameters: -   - - - **v** – 1/True/true or 0/False/false, Remove the volumes +- **v** – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false - Status Codes: +Status Codes: - - **204** – no error - - **400** – bad parameter - - **404** – no such container - - **500** – server error +- **204** – no error +- **400** – bad parameter +- **404** – no such container +- **500** – server error ### Copy files or folders from a container @@ -631,7 +639,7 @@ Remove the container `id` from the filesystem Copy files or folders of container `id` - **Example request**: +**Example request**: POST /containers/4fa6e0f0c678/copy HTTP/1.1 Content-Type: application/json @@ -640,18 +648,18 @@ Copy files or folders of container `id` "Resource":"test.txt" } - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/octet-stream - {{ STREAM }} + {{ TAR STREAM }} - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such container - - **500** – server error +- **200** – no error +- **404** – no such container +- **500** – server error ## 2.2 Images @@ -663,7 +671,7 @@ Copy files or folders of container `id` GET /images/json?all=0 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -697,13 +705,13 @@ Copy files or folders of container `id` `POST /images/create` -Create an image, either by pull it from the registry or by importing it +Create an image, either by pull it from the registry or by importing i - **Example request**: +**Example request**: POST /images/create?fromImage=base HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -717,26 +725,22 @@ Create an image, either by pull it from the registry or by importing it `X-Registry-Auth` header can be used to include a base64-encoded AuthConfig object. - Query Parameters: +Query Parameters: -   +- **fromImage** – name of the image to pull +- **fromSrc** – source to import, - means stdin +- **repo** – repository +- **tag** – tag +- **registry** – the registry to pull from - - **fromImage** – name of the image to pull - - **fromSrc** – source to import, - means stdin - - **repo** – repository - - **tag** – tag - - **registry** – the registry to pull from +Request Headers: - Request Headers: +- **X-Registry-Auth** – base64-encoded AuthConfig object -   +Status Codes: - - **X-Registry-Auth** – base64-encoded AuthConfig object - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Insert a file in an image @@ -744,11 +748,11 @@ Create an image, either by pull it from the registry or by importing it Insert a file from `url` in the image `name` at `path` - **Example request**: +**Example request**: POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -758,17 +762,15 @@ Insert a file from `url` in the image `name` at `path` {"error":"Invalid..."} ... - Query Parameters: +Query Parameters: +- **url** – The url from where the file is taken +- **path** – The path where the file is stored +Status Codes: - - **url** – The url from where the file is taken - - **path** – The path where the file is stored - - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Inspect an image @@ -776,11 +778,11 @@ Insert a file from `url` in the image `name` at `path` Return low-level information on the image `name` - **Example request**: +**Example request**: GET /images/base/json HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -814,11 +816,11 @@ Return low-level information on the image `name` "Size": 6824592 } - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Get the history of an image @@ -826,11 +828,11 @@ Return low-level information on the image `name` Return the history of the image `name` - **Example request**: +**Example request**: GET /images/base/history HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -848,11 +850,11 @@ Return the history of the image `name` } ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Push an image on the registry @@ -860,11 +862,11 @@ Return the history of the image `name` Push the image `name` on the registry - **Example request**: +**Example request**: POST /images/test/push HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -878,14 +880,14 @@ Push the image `name` on the registry   - - **X-Registry-Auth** – include a base64-encoded AuthConfig +- **X-Registry-Auth** – include a base64-encoded AuthConfig object. - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **500** – server error +- **200** – no error +- **404** – no such image +- **500** – server error ### Tag an image into a repository @@ -893,39 +895,38 @@ Push the image `name` on the registry Tag the image `name` into a repository - **Example request**: +**Example request**: - POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 201 OK - Query Parameters: +Query Parameters: -   +- **repo** – The repository to tag in +- **force** – 1/True/true or 0/False/false, default false +- **tag** - The new tag name - - **repo** – The repository to tag in - - **force** – 1/True/true or 0/False/false, default false +Status Codes: - Status Codes: - - - **201** – no error - - **400** – bad parameter - - **404** – no such image - - **409** – conflict - - **500** – server error +- **201** – no error +- **400** – bad parameter +- **404** – no such image +- **409** – conflict +- **500** – server error ### Remove an image `DELETE /images/(name*) : Remove the image `name` from the filesystem - **Example request**: +**Example request**: DELETE /images/test HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-type: application/json @@ -936,12 +937,12 @@ Tag the image `name` into a repository {"Deleted":"53b4f83ac9"} ] - Status Codes: +Status Codes: - - **200** – no error - - **404** – no such image - - **409** – conflict - - **500** – server error +- **200** – no error +- **404** – no such image +- **409** – conflict +- **500** – server error ### Search images @@ -953,11 +954,11 @@ Search for an image on [Docker Hub](https://hub.docker.com). > The response keys have changed from API v1.6 to reflect the JSON > sent by the registry server to the docker daemon's request. - **Example request**: +**Example request**: GET /images/search?term=sshd HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -987,16 +988,14 @@ Search for an image on [Docker Hub](https://hub.docker.com). ... ] - Query Parameters: +Query Parameters: -   +- **term** – term to search - - **term** – term to search +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ## 2.3 Misc @@ -1006,13 +1005,13 @@ Search for an image on [Docker Hub](https://hub.docker.com). Build an image from Dockerfile using a POST body. - **Example request**: +**Example request**: POST /build HTTP/1.1 - {{ STREAM }} + {{ TAR STREAM }} - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1029,28 +1028,23 @@ Build an image from Dockerfile using a POST body. which will be accessible in the build context (See the [*ADD build command*](/reference/builder/#dockerbuilder)). - Query Parameters: +Query Parameters: -   - - - **t** – repository name (and optionally a tag) to be applied to - the resulting image in case of success - - **q** – suppress verbose build output - - **nocache** – do not use the cache when building the image - - **rm** – Remove intermediate containers after a successful build +- **t** – repository name (and optionally a tag) to be applied to + the resulting image in case of success +- **q** – suppress verbose build output +- **nocache** – do not use the cache when building the image +- **rm** – Remove intermediate containers after a successful build Request Headers: -   +- **Content-type** – should be set to `"application/tar"`. +- **X-Registry-Config** – base64-encoded ConfigFile objec - - **Content-type** – should be set to - `"application/tar"`. - - **X-Registry-Config** – base64-encoded ConfigFile object +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Check auth configuration @@ -1058,7 +1052,7 @@ Build an image from Dockerfile using a POST body. Get the default username and email - **Example request**: +**Example request**: POST /auth HTTP/1.1 Content-Type: application/json @@ -1070,15 +1064,16 @@ Get the default username and email "serveraddress":"https://index.docker.io/v1/" } - **Example response**: +**Example response**: HTTP/1.1 200 OK + Content-Type: text/plain - Status Codes: +Status Codes: - - **200** – no error - - **204** – no error - - **500** – server error +- **200** – no error +- **204** – no error +- **500** – server error ### Display system-wide information @@ -1086,11 +1081,11 @@ Get the default username and email Display system-wide information - **Example request**: +**Example request**: GET /info HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1106,10 +1101,10 @@ Display system-wide information "IPv4Forwarding":true } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Show the docker version information @@ -1117,11 +1112,11 @@ Display system-wide information Show the docker version information - **Example request**: +**Example request**: GET /version HTTP/1.1 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1132,10 +1127,10 @@ Show the docker version information "GoVersion":"go1.0.3" } - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Create a new image from a container's changes @@ -1143,7 +1138,7 @@ Show the docker version information Create a new image from a container's changes - **Example request**: +**Example request**: POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 Content-Type: application/json @@ -1174,48 +1169,52 @@ Create a new image from a container's changes } } - **Example response**: +**Example response**: - HTTP/1.1 201 OK - Content-Type: application/vnd.docker.raw-stream + HTTP/1.1 201 Created + Content-Type: application/vnd.docker.raw-stream {"Id":"596069db4bf5"} - Json Parameters: +Json Parameters: +- **config** - the container's configuration +Query Parameters: - - **config** - the container's configuration +- **container** – source container +- **repo** – repository +- **tag** – tag +- **m** – commit message +- **author** – author (e.g., "John Hannibal Smith + <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - Query Parameters: +Status Codes: -   - - - **container** – source container - - **repo** – repository - - **tag** – tag - - **m** – commit message - - **author** – author (e.g., "John Hannibal Smith - <[hannibal@a-team.com](mailto:hannibal%40a-team.com)>") - - Status Codes: - - - **201** – no error - - **404** – no such container - - **500** – server error +- **201** – no error +- **404** – no such container +- **500** – server error ### Monitor Docker's events `GET /events` Get events from docker, either in real time via streaming, or via -polling (using since) +polling (using since). - **Example request**: +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +**Example request**: GET /events?since=1374067924 - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/json @@ -1225,16 +1224,14 @@ polling (using since) {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} - Query Parameters: +Query Parameters: -   +- **since** – timestamp used for polling - - **since** – timestamp used for polling +Status Codes: - Status Codes: - - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Get a tarball containing all images and tags in a repository @@ -1242,21 +1239,23 @@ polling (using since) Get a tarball containing all images and metadata for the repository specified by `name`. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** GET /images/ubuntu/get - **Example response**: +**Example response**: HTTP/1.1 200 OK Content-Type: application/x-tar Binary data stream - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error ### Load a tarball with a set of images and tags into docker @@ -1264,20 +1263,43 @@ Get a tarball containing all images and metadata for the repository specified by Load a set of images and tags into the docker repository. - **Example request** +See the [image tarball format](#image-tarball-format) for more details. + +**Example request** POST /images/load Tarball in body - **Example response**: +**Example response**: HTTP/1.1 200 OK - Status Codes: +Status Codes: - - **200** – no error - - **500** – server error +- **200** – no error +- **500** – server error + +### Image tarball format + +An image tarball contains one directory per image layer (named using its long ID), +each containing three files: + +1. `VERSION`: currently `1.0` - the file format version +2. `json`: detailed layer information, similar to `docker inspect layer_id` +3. `layer.tar`: A tarfile containing the filesystem changes in this layer + +The `layer.tar` file will contain `aufs` style `.wh..wh.aufs` files and directories +for storing attribute changes and deletions. + +If the tarball defines a repository, there will also be a `repositories` file at +the root that contains a list of repository and tag names mapped to layer IDs. + +``` +{"hello-world": + {"latest":"565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"} +} +``` # 3. Going further @@ -1287,21 +1309,21 @@ Here are the steps of `docker run` : - Create the container - - If the status code is 404, it means the image doesn't exists: + - If the status code is 404, it means the image doesn't exist: - - Try to pull it - - Then retry to create the container +- Try to pull it +- Then retry to create the container - Start the container - If you are not in detached mode: - - Attach to the container, using logs=1 (to have stdout and - - stderr from the container's start) and stream=1 +- Attach to the container, using logs=1 (to have stdout and +- stderr from the container's start) and stream=1 - If in detached mode or only stdin is attached: - - Display the container's id +- Display the container's id ## 3.2 Hijacking @@ -1311,6 +1333,6 @@ stdout and stderr on the same socket. This might change in the future. ## 3.3 CORS Requests To enable cross origin requests to the remote api add the flag -"–api-enable-cors" when running docker in daemon mode. +"--api-enable-cors" when running docker in daemon mode. $ docker -d -H="192.168.1.9:2375" --api-enable-cors diff --git a/docs/sources/reference/api/hub_registry_spec.md b/docs/sources/reference/api/hub_registry_spec.md index b2d29ab4af..ee15277a44 100644 --- a/docs/sources/reference/api/hub_registry_spec.md +++ b/docs/sources/reference/api/hub_registry_spec.md @@ -111,7 +111,7 @@ supports: It's possible to run: - $ docker pull https:///repositories/samalba/busybox + $ sudo docker pull https:///repositories/samalba/busybox In this case, Docker bypasses the Docker Hub. However the security is not guaranteed (in case Registry A is corrupted) because there won't be any @@ -133,52 +133,61 @@ and for an active account. 1. (Docker -> Docker Hub) GET /v1/repositories/foo/bar/images: - **Headers**: +**Headers**: + Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== X-Docker-Token: true - - **Action**: + +**Action**: + (looking up the foo/bar in db and gets images and checksums for that repo (all if no tag is specified, if tag, only checksums for those tags) see part 4.4.1) 2. (Docker Hub -> Docker) HTTP 200 OK - **Headers**: +**Headers**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=write X-Docker-Endpoints: registry.docker.io [,registry2.docker.io] - - **Body**: + +**Body**: + Jsonified checksums (see part 4.4.1) 3. (Docker -> Registry) GET /v1/repositories/foo/bar/tags/latest - **Headers**: +**Headers**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=write 4. (Registry -> Docker Hub) GET /v1/repositories/foo/bar/images - **Headers**: +**Headers**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=read - - **Body**: + +**Body**: + - - **Action**: + +**Action**: + (Lookup token see if they have access to pull.) - + If good: HTTP 200 OK Docker Hub will invalidate the token - + If bad: HTTP 401 Unauthorized 5. (Docker -> Registry) GET /v1/images/928374982374/ancestry - **Action**: +**Action**: + (for each image id returned in the registry, fetch /json + /layer) > **Note**: @@ -220,92 +229,108 @@ the end). 1. (Docker -> Docker Hub) PUT /v1/repositories/foo/bar/ - **Headers**: +**Headers**: + Authorization: Basic sdkjfskdjfhsdkjfh== X-Docker-Token: true - **Action**: - - in Docker Hub, we allocated a new repository, and set to - initialized +**Action**: - **Body**: - (The body contains the list of images that are going to be - pushed, with empty checksums. The checksums will be set at - the end of the push): +- in Docker Hub, we allocated a new repository, and set to + initialized + +**Body**: + +(The body contains the list of images that are going to be +pushed, with empty checksums. The checksums will be set at +the end of the push): [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}] 2. (Docker Hub -> Docker) 200 Created - **Headers**: - - WWW-Authenticate: Token +**Headers**: + + WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=write - - X-Docker-Endpoints: registry.docker.io [, - registry2.docker.io] + X-Docker-Endpoints: registry.docker.io [, registry2.docker.io] 3. (Docker -> Registry) PUT /v1/images/98765432_parent/json - **Headers**: +**Headers**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=write 4. (Registry->Docker Hub) GET /v1/repositories/foo/bar/images - **Headers**: +**Headers**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=write - **Action**: - - Docker Hub: - will invalidate the token. - - Registry: - grants a session (if token is approved) and fetches - the images id +**Action**: + +- Docker Hub: + will invalidate the token. +- Registry: + grants a session (if token is approved) and fetches + the images id 5. (Docker -> Registry) PUT /v1/images/98765432_parent/json - **Headers**:: - - Authorization: Token +**Headers**: + + Authorization: Token signature=123abc,repository=”foo/bar”,access=write - - Cookie: (Cookie provided by the Registry) + Cookie: (Cookie provided by the Registry) 6. (Docker -> Registry) PUT /v1/images/98765432/json - **Headers**: - - Cookie: (Cookie provided by the Registry) +**Headers**: + + Cookie: (Cookie provided by the Registry) 7. (Docker -> Registry) PUT /v1/images/98765432_parent/layer - **Headers**: - - Cookie: (Cookie provided by the Registry) +**Headers**: + + Cookie: (Cookie provided by the Registry) 8. (Docker -> Registry) PUT /v1/images/98765432/layer - **Headers**: +**Headers**: + X-Docker-Checksum: sha256:436745873465fdjkhdfjkgh 9. (Docker -> Registry) PUT /v1/repositories/foo/bar/tags/latest - **Headers**: - - Cookie: (Cookie provided by the Registry) +**Headers**: + + Cookie: (Cookie provided by the Registry) + +**Body**: - **Body**: “98765432” 10. (Docker -> Docker Hub) PUT /v1/repositories/foo/bar/images - **Headers**: +**Headers**: + Authorization: Basic 123oislifjsldfj== X-Docker-Endpoints: registry1.docker.io (no validation on this right now) - **Body**: +**Body**: + (The image, id`s, tags and checksums) [{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}] - **Return**: HTTP 204 +**Return**: + + HTTP 204 > **Note:** If push fails and they need to start again, what happens in the Docker Hub, > there will already be a record for the namespace/name, but it will be @@ -344,43 +369,49 @@ nice clean way to do that. Here is the workflow. 1. (Docker -> Docker Hub) DELETE /v1/repositories/foo/bar/ - **Headers**: +**Headers**: + Authorization: Basic sdkjfskdjfhsdkjfh== X-Docker-Token: true - **Action**: - - in Docker Hub, we make sure it is a valid repository, and set - to deleted (logically) +**Action**: + +- in Docker Hub, we make sure it is a valid repository, and set + to deleted (logically) + +**Body**: - **Body**: Empty 2. (Docker Hub -> Docker) 202 Accepted - **Headers**: - - WWW-Authenticate: Token +**Headers**: + + WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=delete - - X-Docker-Endpoints: registry.docker.io [, - registry2.docker.io] + X-Docker-Endpoints: registry.docker.io [, registry2.docker.io] # list of endpoints where this repo lives. 3. (Docker -> Registry) DELETE /v1/repositories/foo/bar/ - **Headers**: +**Headers**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=delete 4. (Registry->Docker Hub) PUT /v1/repositories/foo/bar/auth - **Headers**: +**Headers**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=delete - **Action**: - - Docker Hub: - will invalidate the token. - - Registry: - deletes the repository (if token is approved) +**Action**: + +- Docker Hub: + will invalidate the token. +- Registry: + deletes the repository (if token is approved) 5. (Registry -> Docker) 200 OK @@ -389,14 +420,18 @@ nice clean way to do that. Here is the workflow. 6. (Docker -> Docker Hub) DELETE /v1/repositories/foo/bar/ - **Headers**: +**Headers**: + Authorization: Basic 123oislifjsldfj== X-Docker-Endpoints: registry-1.docker.io (no validation on this right now) - - **Body**: + +**Body**: + Empty - - **Return**: HTTP 200 + +**Return**: + + HTTP 200 ## How to use the Registry in standalone mode @@ -412,7 +447,7 @@ The Docker Hub has two main purposes (along with its fancy social features): - Authenticate a user as a repos owner (for a central referenced repository) -### Without an Docker Hub +### Without a Docker Hub Using the Registry without the Docker Hub can be useful to store the images on a private network without having to rely on an external entity @@ -433,7 +468,7 @@ As hinted previously, a standalone registry can also be implemented by any HTTP server handling GET/PUT requests (or even only GET requests if no write access is necessary). -### With an Docker Hub +### With a Docker Hub The Docker Hub data needed by the Registry are simple: @@ -478,16 +513,20 @@ file is empty. POST /v1/users: - **Body**: +**Body**: + {"email": "[sam@docker.com](mailto:sam%40docker.com)", "password": "toto42", "username": "foobar"`} - **Validation**: - - **username**: min 4 character, max 30 characters, must match the - regular expression [a-z0-9_]. - - **password**: min 5 characters +**Validation**: - **Valid**: return HTTP 200 +- **username**: min 4 character, max 30 characters, must match the + regular expression [a-z0-9_]. +- **password**: min 5 characters + +**Valid**: + + return HTTP 201 Errors: HTTP 400 (we should create error codes for possible errors) - invalid json - missing field - wrong format (username, password, email, @@ -501,7 +540,8 @@ etc) - forbidden name - name already exists PUT /v1/users/ - **Body**: +**Body**: + {"password": "toto"} > **Note**: @@ -515,10 +555,10 @@ validate credentials. HTTP Basic Auth for now, maybe change in future. GET /v1/users - **Return**: - - Valid: HTTP 200 - - Invalid login: HTTP 401 - - Account inactive: HTTP 403 Account is not Active +**Return**: +- Valid: HTTP 200 +- Invalid login: HTTP 401 +- Account inactive: HTTP 403 Account is not Active ### Tags (Registry) @@ -543,14 +583,15 @@ GET /v1/repositories///tags “0.1.1”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087” } - **4.3.2 Read the content of a tag (resolve the image id):** +**4.3.2 Read the content of a tag (resolve the image id):** GET /v1/repositories///tags/ - **Return**: +**Return**: + "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f" - **4.3.3 Delete a tag (registry):** +**4.3.3 Delete a tag (registry):** DELETE /v1/repositories///tags/ @@ -577,14 +618,17 @@ You always add images, you never remove them. PUT /v1/repositories///images - **Body**: +**Body**: + [ {“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”} ] - **Return**: 204 +**Return**: + + 204 ### Repositories @@ -640,28 +684,32 @@ You have 3 options: 1. Provide user credentials and ask for a token - **Header**: - - Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - - X-Docker-Token: true - - In this case, along with the 200 response, you'll get a new token - (if user auth is ok): If authorization isn't correct you get a 401 - response. If account isn't active you will get a 403 response. - - **Response**: - - 200 OK - - X-Docker-Token: Token - signature=123abc,repository=”foo/bar”,access=read - +**Header**: + + Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + X-Docker-Token: true + +In this case, along with the 200 response, you'll get a new token +(if user auth is ok): If authorization isn't correct you get a 401 +response. If account isn't active you will get a 403 response. + +**Response**: + + 200 OK + X-Docker-Token: Token + signature=123abc,repository=”foo/bar”,access=read + 2. Provide user credentials only - **Header**: +**Header**: + Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 3. Provide Token - **Header**: +**Header**: + Authorization: Token signature=123abc,repository=”foo/bar”,access=read diff --git a/docs/sources/reference/api/registry_api.md b/docs/sources/reference/api/registry_api.md index 49776b9b18..1ae37dba6d 100644 --- a/docs/sources/reference/api/registry_api.md +++ b/docs/sources/reference/api/registry_api.md @@ -73,13 +73,13 @@ The default namespace for a private repository is `library`. ## Images -### Layer +### Get image layer `GET /v1/images/(image_id)/layer` Get image layer for a given `image_id` - **Example Request**: +**Example Request**: GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/layer HTTP/1.1 Host: registry-1.docker.io @@ -87,11 +87,11 @@ Get image layer for a given `image_id` Content-Type: application/json Authorization: Token signature=123abc,repository="foo/bar",access=read - Parameters: +Parameters: - - **image_id** – the id for the layer you want to get +- **image_id** – the id for the layer you want to get - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -100,17 +100,19 @@ Get image layer for a given `image_id` {layer binary data stream} - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Image not found +- **200** – OK +- **401** – Requires authorization +- **404** – Image not found + +### Put image layer `PUT /v1/images/(image_id)/layer` Put image layer for a given `image_id` - **Example Request**: +**Example Request**: PUT /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/layer HTTP/1.1 Host: registry-1.docker.io @@ -119,11 +121,11 @@ Put image layer for a given `image_id` {layer binary data stream} - Parameters: +Parameters: - - **image_id** – the id for the layer you want to get +- **image_id** – the id for the layer you want to get - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -132,19 +134,21 @@ Put image layer for a given `image_id` "" - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Image not found +- **200** – OK +- **401** – Requires authorization +- **404** – Image not found ## Image +### Put image layer + `PUT /v1/images/(image_id)/json` Put image for a given `image_id` - **Example Request**: +**Example Request**: PUT /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/json HTTP/1.1 Host: registry-1.docker.io @@ -183,11 +187,11 @@ Put image for a given `image_id` docker_version: "0.1.7" } - Parameters: +Parameters: - - **image_id** – the id for the layer you want to get +- **image_id** – the id for the layer you want to get - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -196,16 +200,18 @@ Put image for a given `image_id` "" - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization +- **200** – OK +- **401** – Requires authorization + +### Get image layer `GET /v1/images/(image_id)/json` Get image for a given `image_id` - **Example Request**: +**Example Request**: GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/json HTTP/1.1 Host: registry-1.docker.io @@ -213,11 +219,11 @@ Get image for a given `image_id` Content-Type: application/json Cookie: (Cookie provided by the Registry) - Parameters: +Parameters: - - **image_id** – the id for the layer you want to get +- **image_id** – the id for the layer you want to get - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -257,19 +263,21 @@ Get image for a given `image_id` docker_version: "0.1.7" } - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Image not found +- **200** – OK +- **401** – Requires authorization +- **404** – Image not found ## Ancestry +### Get image ancestry + `GET /v1/images/(image_id)/ancestry` Get ancestry for an image given an `image_id` - **Example Request**: +**Example Request**: GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/ancestry HTTP/1.1 Host: registry-1.docker.io @@ -277,11 +285,11 @@ Get ancestry for an image given an `image_id` Content-Type: application/json Cookie: (Cookie provided by the Registry) - Parameters: +Parameters: - - **image_id** – the id for the layer you want to get +- **image_id** – the id for the layer you want to get - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -293,19 +301,21 @@ Get ancestry for an image given an `image_id` "bfa4c5326bc764280b0863b46a4b20d940bc1897ef9c1dfec060604bdc383280", "6ab5893c6927c15a15665191f2c6cf751f5056d8b95ceee32e43c5e8a3648544"] - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Image not found +- **200** – OK +- **401** – Requires authorization +- **404** – Image not found ## Tags +### List repository tags + `GET /v1/repositories/(namespace)/(repository)/tags` Get all of the tags for the given repo. - **Example Request**: +**Example Request**: GET /v1/repositories/reynholm/help-system-server/tags HTTP/1.1 Host: registry-1.docker.io @@ -314,12 +324,12 @@ Get all of the tags for the given repo. X-Docker-Registry-Version: 0.6.0 Cookie: (Cookie provided by the Registry) - Parameters: +Parameters: - - **namespace** – namespace for the repo - - **repository** – name for the repo +- **namespace** – namespace for the repo +- **repository** – name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -331,17 +341,19 @@ Get all of the tags for the given repo. "0.1.1": "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087" } - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Repository not found +- **200** – OK +- **401** – Requires authorization +- **404** – Repository not found + +### Get image id for a particular tag `GET /v1/repositories/(namespace)/(repository)/tags/(tag*)` Get a tag for the given repo. - **Example Request**: +**Example Request**: GET /v1/repositories/reynholm/help-system-server/tags/latest HTTP/1.1 Host: registry-1.docker.io @@ -350,13 +362,13 @@ Get a tag for the given repo. X-Docker-Registry-Version: 0.6.0 Cookie: (Cookie provided by the Registry) - Parameters: +Parameters: - - **namespace** – namespace for the repo - - **repository** – name for the repo - - **tag** – name of tag you want to get +- **namespace** – namespace for the repo +- **repository** – name for the repo +- **tag** – name of tag you want to get - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -365,17 +377,19 @@ Get a tag for the given repo. "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f" - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Tag not found +- **200** – OK +- **401** – Requires authorization +- **404** – Tag not found + +### Delete a repository tag `DELETE /v1/repositories/(namespace)/(repository)/tags/(tag*)` Delete the tag for the repo - **Example Request**: +**Example Request**: DELETE /v1/repositories/reynholm/help-system-server/tags/latest HTTP/1.1 Host: registry-1.docker.io @@ -383,13 +397,13 @@ Delete the tag for the repo Content-Type: application/json Cookie: (Cookie provided by the Registry) - Parameters: +Parameters: - - **namespace** – namespace for the repo - - **repository** – name for the repo - - **tag** – name of tag you want to delete +- **namespace** – namespace for the repo +- **repository** – name for the repo +- **tag** – name of tag you want to delete - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -398,17 +412,19 @@ Delete the tag for the repo "" - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Tag not found +- **200** – OK +- **401** – Requires authorization +- **404** – Tag not found + +### Set a tag for a specified image id `PUT /v1/repositories/(namespace)/(repository)/tags/(tag*)` Put a tag for the given repo. - **Example Request**: +**Example Request**: PUT /v1/repositories/reynholm/help-system-server/tags/latest HTTP/1.1 Host: registry-1.docker.io @@ -418,13 +434,13 @@ Put a tag for the given repo. "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f" - Parameters: +Parameters: - - **namespace** – namespace for the repo - - **repository** – name for the repo - - **tag** – name of tag you want to add +- **namespace** – namespace for the repo +- **repository** – name for the repo +- **tag** – name of tag you want to add - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -433,20 +449,22 @@ Put a tag for the given repo. "" - Status Codes: +Status Codes: - - **200** – OK - - **400** – Invalid data - - **401** – Requires authorization - - **404** – Image not found +- **200** – OK +- **400** – Invalid data +- **401** – Requires authorization +- **404** – Image not found ## Repositories +### Delete a repository + `DELETE /v1/repositories/(namespace)/(repository)/` Delete a repository - **Example Request**: +**Example Request**: DELETE /v1/repositories/reynholm/help-system-server/ HTTP/1.1 Host: registry-1.docker.io @@ -456,12 +474,12 @@ Delete a repository "" - Parameters: +Parameters: - - **namespace** – namespace for the repo - - **repository** – name for the repo +- **namespace** – namespace for the repo +- **repository** – name for the repo - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -470,20 +488,22 @@ Delete a repository "" - Status Codes: +Status Codes: - - **200** – OK - - **401** – Requires authorization - - **404** – Repository not found +- **200** – OK +- **401** – Requires authorization +- **404** – Repository not found ## Status +### Status check for registry + `GET /v1/_ping` Check status of the registry. This endpoint is also used to determine if the registry supports SSL. - **Example Request**: +**Example Request**: GET /v1/_ping HTTP/1.1 Host: registry-1.docker.io @@ -492,7 +512,7 @@ determine if the registry supports SSL. "" - **Example Response**: +**Example Response**: HTTP/1.1 200 Vary: Accept @@ -501,13 +521,12 @@ determine if the registry supports SSL. "" - Status Codes: +Status Codes: - - **200** – OK +- **200** – OK ## Authorization This is where we describe the authorization process, including the tokens and cookies. -TODO: add more info. diff --git a/docs/sources/reference/api/registry_api_client_libraries.md b/docs/sources/reference/api/registry_api_client_libraries.md new file mode 100644 index 0000000000..7fe986204a --- /dev/null +++ b/docs/sources/reference/api/registry_api_client_libraries.md @@ -0,0 +1,35 @@ +page_title: Registry API Client Libraries +page_description: Various client libraries available to use with the Docker registry API +page_keywords: API, Docker, index, registry, REST, documentation, clients, C#, Erlang, Go, Groovy, Java, JavaScript, Perl, PHP, Python, Ruby, Rust, Scala + +# Docker Registry API Client Libraries + +These libraries have not been tested by the Docker maintainers for +compatibility. Please file issues with the library owners. If you find +more library implementations, please list them in Docker doc bugs and we +will add the libraries here. + + + + + + + + + + + + + + + + + + + + + + + + +
Language/FrameworkNameRepositoryStatus
JavaScript (AngularJS) WebUIdocker-registry-frontendhttps://github.com/kwk/docker-registry-frontendActive
diff --git a/docs/sources/reference/api/remote_api_client_libraries.md b/docs/sources/reference/api/remote_api_client_libraries.md index 8f50804368..71bd2ebfc1 100644 --- a/docs/sources/reference/api/remote_api_client_libraries.md +++ b/docs/sources/reference/api/remote_api_client_libraries.md @@ -1,10 +1,10 @@ page_title: Remote API Client Libraries page_description: Various client libraries available to use with the Docker remote API -page_keywords: API, Docker, index, registry, REST, documentation, clients, Python, Ruby, JavaScript, Erlang, Go +page_keywords: API, Docker, index, registry, REST, documentation, clients, C#, Erlang, Go, Groovy, Java, JavaScript, Perl, PHP, Python, Ruby, Rust, Scala # Docker Remote API Client Libraries -These libraries have not been tested by the Docker Maintainers for +These libraries have not been tested by the Docker maintainers for compatibility. Please file issues with the library owners. If you find more library implementations, please list them in Docker doc bugs and we will add the libraries here. @@ -25,59 +25,9 @@ will add the libraries here. - Python - docker-py - https://github.com/dotcloud/docker-py - Active - - - Ruby - docker-client - https://github.com/geku/docker-client - Outdated - - - Ruby - docker-api - https://github.com/swipely/docker-api - Active - - - JavaScript (NodeJS) - dockerode - https://github.com/apocas/dockerode - Install via NPM: npm install dockerode - Active - - - JavaScript (NodeJS) - docker.io - https://github.com/appersonlabs/docker.io - Install via NPM: npm install docker.io - Active - - - JavaScript - docker-js - https://github.com/dgoujard/docker-js - Outdated - - - JavaScript (Angular) WebUI - docker-cp - https://github.com/13W/docker-cp - Active - - - JavaScript (Angular) WebUI - dockerui - https://github.com/crosbymichael/dockerui - Active - - - Java - docker-java - https://github.com/docker-java/docker-java + C# + Docker.DotNet + https://github.com/ahmetalpbalkan/Docker.DotNet Active @@ -99,39 +49,15 @@ will add the libraries here. Active - PHP - Alvine - http://pear.alvine.io/ (alpha) + Groovy + docker-client + https://github.com/gesellix-docker/docker-client Active - PHP - Docker-PHP - http://stage1.github.io/docker-php/ - Active - - - Perl - Net::Docker - https://metacpan.org/pod/Net::Docker - Active - - - Perl - Eixo::Docker - https://github.com/alambike/eixo-docker - Active - - - Scala - tugboat - https://github.com/softprops/tugboat - Active - - - Scala - reactive-docker - https://github.com/almoehi/reactive-docker + Java + docker-java + https://github.com/docker-java/docker-java Active @@ -141,16 +67,102 @@ will add the libraries here. Active - Groovy - docker-client - https://github.com/gesellix-docker/docker-client - Active - - Java jclouds-docker https://github.com/jclouds/jclouds-labs/tree/master/docker Active + + JavaScript (NodeJS) + dockerode + https://github.com/apocas/dockerode + Install via NPM: npm install dockerode + Active + + + JavaScript (NodeJS) + docker.io + https://github.com/appersonlabs/docker.io + Install via NPM: npm install docker.io + Active + + + JavaScript + docker-js + https://github.com/dgoujard/docker-js + Outdated + + + JavaScript (Angular) WebUI + docker-cp + https://github.com/13W/docker-cp + Active + + + JavaScript (Angular) WebUI + dockerui + https://github.com/crosbymichael/dockerui + Active + + + Perl + Net::Docker + https://metacpan.org/pod/Net::Docker + Active + + + Perl + Eixo::Docker + https://github.com/alambike/eixo-docker + Active + + + PHP + Alvine + http://pear.alvine.io/ (alpha) + Active + + + PHP + Docker-PHP + http://stage1.github.io/docker-php/ + Active + + + Python + docker-py + https://github.com/dotcloud/docker-py + Active + + + Ruby + docker-api + https://github.com/swipely/docker-api + Active + + + Ruby + docker-client + https://github.com/geku/docker-client + Outdated + + + Rust + docker-rust + https://github.com/abh1nav/docker-rust + Active + + + Scala + tugboat + https://github.com/softprops/tugboat + Active + + + Scala + reactive-docker + https://github.com/almoehi/reactive-docker + Active + diff --git a/docs/sources/reference/builder.md b/docs/sources/reference/builder.md index 796d07d98e..2678a87a19 100644 --- a/docs/sources/reference/builder.md +++ b/docs/sources/reference/builder.md @@ -11,6 +11,11 @@ Docker image. By calling `docker build` from your terminal, you can have Docker build your image step by step, executing the instructions successively. +This page discusses the specifics of all the instructions you can use in your +`Dockerfile`. To further help you write a clear, readable, maintainable +`Dockerfile`, we've also written a [`Dockerfile` Best Practices +guide](/articles/dockerfile_best-practices). + ## Usage To [*build*](../commandline/cli/#cli-build) an image from a source repository, @@ -55,9 +60,11 @@ to be created - so `RUN cd /tmp` will not have any effect on the next instructions. Whenever possible, Docker will re-use the intermediate images, -accelerating `docker build` significantly (indicated by `Using cache`): +accelerating `docker build` significantly (indicated by `Using cache` - +see the [`Dockerfile` Best Practices +guide](/articles/dockerfile_best-practices/#build-cache) for more information): - $ docker build -t SvenDowideit/ambassador . + $ sudo docker build -t SvenDowideit/ambassador . Uploading context 10.24 kB Uploading context Step 1 : FROM docker-ut @@ -109,7 +116,7 @@ The following example shows the use of the `.dockerignore` file to exclude the `.git` directory from the context. Its effect can be seen in the changed size of the uploaded context. - $ docker build . + $ sudo docker build . Uploading context 18.829 MB Uploading context Step 0 : FROM busybox @@ -119,7 +126,7 @@ the uploaded context. ---> 99cc1ad10469 Successfully built 99cc1ad10469 $ echo ".git" > .dockerignore - $ docker build . + $ sudo docker build . Uploading context 6.76 MB Uploading context Step 0 : FROM busybox @@ -163,7 +170,7 @@ generated images. RUN has 2 forms: -- `RUN ` (the command is run in a shell - `/bin/sh -c`) +- `RUN ` (the command is run in a shell - `/bin/sh -c` - *shell* form) - `RUN ["executable", "param1", "param2"]` (*exec* form) The `RUN` instruction will execute any commands in a new layer on top of the @@ -182,11 +189,25 @@ commands using a base image that does not contain `/bin/sh`. > passing in the desired shell. For example, > `RUN ["/bin/bash", "-c", "echo hello"]` +> **Note**: +> The *exec* form is parsed as a JSON array, which means that +> you must use double-quotes (") around words not single-quotes ('). + +> **Note**: +> Unlike the *shell* form, the *exec* form does not invoke a command shell. +> This means that normal shell processing does not happen. For example, +> `CMD [ "echo", "$HOME" ]` will not do variable substitution on `$HOME`. +> If you want shell processing then either use the *shell* form or execute +> a shell directly, for example: `CMD [ "sh", "-c", "echo", "$HOME" ]`. + The cache for `RUN` instructions isn't invalidated automatically during -the next build. The cache for an instruction like `RUN apt-get -dist-upgrade -y` will be reused during the next build. The cache for -`RUN` instructions can be invalidated by using the `--no-cache` flag, -for example `docker build --no-cache`. +the next build. The cache for an instruction like +`RUN apt-get dist-upgrade -y` will be reused during the next build. The +cache for `RUN` instructions can be invalidated by using the `--no-cache` +flag, for example `docker build --no-cache`. + +See the [`Dockerfile` Best Practices +guide](/articles/dockerfile_best-practices/#build-cache) for more information. The cache for `RUN` instructions can be invalidated by `ADD` instructions. See [below](#add) for details. @@ -202,9 +223,9 @@ The cache for `RUN` instructions can be invalidated by `ADD` instructions. See The `CMD` instruction has three forms: -- `CMD ["executable","param1","param2"]` (like an *exec*, this is the preferred form) +- `CMD ["executable","param1","param2"]` (*exec* form, this is the preferred form) - `CMD ["param1","param2"]` (as *default parameters to ENTRYPOINT*) -- `CMD command param1 param2` (as a *shell*) +- `CMD command param1 param2` (*shell* form) There can only be one `CMD` instruction in a `Dockerfile`. If you list more than one `CMD` then only the last `CMD` will take effect. @@ -219,6 +240,17 @@ instruction as well. > instruction, both the `CMD` and `ENTRYPOINT` instructions should be specified > with the JSON array format. +> **Note**: +> The *exec* form is parsed as a JSON array, which means that +> you must use double-quotes (") around words not single-quotes ('). + +> **Note**: +> Unlike the *shell* form, the *exec* form does not invoke a command shell. +> This means that normal shell processing does not happen. For example, +> `CMD [ "echo", "$HOME" ]` will not do variable substitution on `$HOME`. +> If you want shell processing then either use the *shell* form or execute +> a shell directly, for example: `CMD [ "sh", "-c", "echo", "$HOME" ]`. + When used in the shell or exec formats, the `CMD` instruction sets the command to be executed when running the image. @@ -255,7 +287,10 @@ default specified in `CMD`. The `EXPOSE` instructions informs Docker that the container will listen on the specified network ports at runtime. Docker uses this information to interconnect containers using links (see the [Docker User -Guide](/userguide/dockerlinks)). +Guide](/userguide/dockerlinks)). Note that `EXPOSE` only works for +inter-container links. It doesn't make ports accessible from the host. To +expose ports to the host, at runtime, +[use the `-p` flag](/userguide/dockerlinks). ## ENV @@ -276,15 +311,24 @@ change them using `docker run --env =`. ## ADD - ADD + ADD ... -The `ADD` instruction will copy new files from `` and add them to the -container's filesystem at path ``. +The `ADD` instruction copies new files,directories or remote file URLs to +the filesystem of the container from `` and add them to the at +path ``. -`` must be the path to a file or directory relative to the source directory -being built (also called the *context* of the build) or a remote file URL. +Multiple `` resource may be specified but if they are files or +directories then they must be relative to the source directory that is +being built (the context of the build). -`` is the absolute path to which the source will be copied inside the +Each `` may contain wildcards and matching will be done using Go's +[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. +For most command line uses this should act as expected, for example: + + ADD hom* /mydir/ # adds all files starting with "hom" + ADD hom?.txt /mydir/ # ? is replaced with any single character + +The `` is the absolute path to which the source will be copied inside the destination container. All new files and directories are created with a UID and GID of 0. @@ -310,6 +354,9 @@ have permissions of 600. > The first encountered `ADD` instruction will invalidate the cache for all > following instructions from the Dockerfile if the contents of `` have > changed. This includes invalidating the cache for `RUN` instructions. +> See the [`Dockerfile` Best Practices +guide](/articles/dockerfile_best-practices/#build-cache) for more information. + The copy obeys the following rules: @@ -345,6 +392,10 @@ The copy obeys the following rules: will be considered a directory and the contents of `` will be written at `/base()`. +- If multiple `` resources are specified, either directly or due to the + use of a wildcard, then `` must be a directory, and it must end with + a slash `/`. + - If `` does not end with a trailing slash, it will be considered a regular file and the contents of `` will be written at ``. @@ -353,15 +404,24 @@ The copy obeys the following rules: ## COPY - COPY + COPY ... -The `COPY` instruction will copy new files from `` and add them to the -container's filesystem at path ``. +The `COPY` instruction copies new files,directories or remote file URLs to +the filesystem of the container from `` and add them to the at +path ``. -`` must be the path to a file or directory relative to the source directory -being built (also called the *context* of the build). +Multiple `` resource may be specified but if they are files or +directories then they must be relative to the source directory that is being +built (the context of the build). -`` is the absolute path to which the source will be copied inside the +Each `` may contain wildcards and matching will be done using Go's +[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. +For most command line uses this should act as expected, for example: + + COPY hom* /mydir/ # adds all files starting with "hom" + COPY hom?.txt /mydir/ # ? is replaced with any single character + +The `` is the absolute path to which the source will be copied inside the destination container. All new files and directories are created with a UID and GID of 0. @@ -385,6 +445,10 @@ The copy obeys the following rules: will be considered a directory and the contents of `` will be written at `/base()`. +- If multiple `` resources are specified, either directly or due to the + use of a wildcard, then `` must be a directory, and it must end with + a slash `/`. + - If `` does not end with a trailing slash, it will be considered a regular file and the contents of `` will be written at ``. @@ -396,9 +460,9 @@ The copy obeys the following rules: ENTRYPOINT has two forms: - `ENTRYPOINT ["executable", "param1", "param2"]` - (like an *exec*, the preferred form) + (*exec* form, the preferred form) - `ENTRYPOINT command param1 param2` - (as a *shell*) + (*shell* form) There can only be one `ENTRYPOINT` in a `Dockerfile`. If you have more than one `ENTRYPOINT`, then only the last one in the `Dockerfile` will @@ -434,6 +498,17 @@ optional but default, you could use a `CMD` instruction: CMD ["-l"] ENTRYPOINT ["ls"] +> **Note**: +> The *exec* form is parsed as a JSON array, which means that +> you must use double-quotes (") around words not single-quotes ('). + +> **Note**: +> Unlike the *shell* form, the *exec* form does not invoke a command shell. +> This means that normal shell processing does not happen. For example, +> `CMD [ "echo", "$HOME" ]` will not do variable substitution on `$HOME`. +> If you want shell processing then either use the *shell* form or execute +> a shell directly, for example: `CMD [ "sh", "-c", "echo", "$HOME" ]`. + > **Note**: > It is preferable to use the JSON array format for specifying > `ENTRYPOINT` instructions. @@ -445,9 +520,14 @@ optional but default, you could use a `CMD` instruction: The `VOLUME` instruction will create a mount point with the specified name and mark it as holding externally mounted volumes from native host or other containers. The value can be a JSON array, `VOLUME ["/var/log/"]`, or a plain -string, `VOLUME /var/log`. For more information/examples and mounting -instructions via the Docker client, refer to [*Share Directories via Volumes*]( -/userguide/dockervolumes/#volume-def) documentation. +string with multiple arguments, such as `VOLUME /var/log` or `VOLUME /var/log +/var/db`. For more information/examples and mounting instructions via the +Docker client, refer to [*Share Directories via Volumes*](/userguide/dockervolumes/#volume-def) +documentation. + +> **Note**: +> The list is parsed a JSON array, which means that +> you must use double-quotes (") around words not single-quotes ('). ## USER @@ -472,9 +552,19 @@ instruction. For example: WORKDIR c RUN pwd -The output of the final `pwd` command in this Dockerfile would be +The output of the final `pwd` command in this `Dockerfile` would be `/a/b/c`. +The `WORKDIR` instruction can resolve environment variables previously set using +`ENV`. You can only use environment variables explicitly set in the `Dockerfile`. +For example: + + ENV DIRPATH /path + WORKDIR $DIRPATH/$DIRNAME + +The output of the final `pwd` command in this `Dockerfile` would be +`/path/$DIRNAME` + ## ONBUILD ONBUILD [INSTRUCTION] diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 7b9e2ab610..86f02b6cf1 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -35,11 +35,11 @@ will set the value to the opposite of the default value. Options like `-a=[]` indicate they can be specified multiple times: - $ docker run -a stdin -a stdout -a stderr -i -t ubuntu /bin/bash + $ sudo docker run -a stdin -a stdout -a stderr -i -t ubuntu /bin/bash Sometimes this can use a more complex value string, as for `-v`: - $ docker run -v /host:/container example/mysql + $ sudo docker run -v /host:/container example/mysql ### Strings and Integers @@ -49,7 +49,11 @@ expect an integer, and they can only be specified once. ## daemon - Usage of docker: + Usage: docker [OPTIONS] COMMAND [arg...] + + A self-sufficient runtime for linux containers. + + Options: --api-enable-cors=false Enable CORS headers in the remote API -b, --bridge="" Attach containers to a pre-existing network bridge use 'none' to disable container networking @@ -59,18 +63,21 @@ expect an integer, and they can only be specified once. --dns=[] Force Docker to use specific DNS servers --dns-search=[] Force Docker to use specific DNS search domains -e, --exec-driver="native" Force the Docker runtime to use a specific exec driver + --fixed-cidr="" IPv4 subnet for fixed IPs (ex: 10.20.0.0/16) + this subnet must be nested in the bridge subnet (which is defined by -b or --bip) -G, --group="docker" Group to assign the unix socket specified by -H when running in daemon mode use '' (the empty string) to disable setting of a group -g, --graph="/var/lib/docker" Path to use as the root of the Docker runtime - -H, --host=[] The socket(s) to bind to in daemon mode - specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd. + -H, --host=[] The socket(s) to bind to in daemon mode or connect to in client mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd. --icc=true Enable inter-container communication --ip=0.0.0.0 Default IP address to use when binding container ports --ip-forward=true Enable net.ipv4.ip_forward + --ip-masq=true Enable IP masquerading for bridge's IP range --iptables=true Enable Docker's addition of iptables rules --mtu=0 Set the containers network MTU if no value is provided: default to the default route MTU or 1500 if no default route is available -p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file + --registry-mirror=[] Specify a preferred Docker registry mirror -s, --storage-driver="" Force the Docker runtime to use a specific storage driver --selinux-enabled=false Enable selinux support. SELinux does not presently support the BTRFS storage driver --storage-opt=[] Set storage driver options @@ -87,36 +94,106 @@ The Docker daemon is the persistent process that manages containers. Docker uses the same binary for both the daemon and client. To run the daemon you provide the `-d` flag. -To force Docker to use devicemapper as the storage driver, use + +To run the daemon with debug output, use `docker -d -D`. + +### Daemon socket option + +The Docker daemon can listen for [Docker Remote API](reference/api/docker_remote_api/) +requests via three different types of Socket: `unix`, `tcp`, and `fd`. + +By default, a `unix` domain socket (or IPC socket) is created at `/var/run/docker.sock`, +requiring either `root` permission, or `docker` group membership. + +If you need to access the Docker daemon remotely, you need to enable the `tcp` +Socket. Beware that the default setup provides un-encrypted and un-authenticated +direct access to the Docker daemon - and should be secured either using the +[built in https encrypted socket](/articles/https/), or by putting a secure web +proxy in front of it. You can listen on port `2375` on all network interfaces +with `-H tcp://0.0.0.0:2375`, or on a particular network interface using its IP +address: `-H tcp://192.168.59.103:2375`. + +On Systemd based systems, you can communicate with the daemon via +[systemd socket activation](http://0pointer.de/blog/projects/socket-activation.html), use +`docker -d -H fd://`. Using `fd://` will work perfectly for most setups but +you can also specify individual sockets: `docker -d -H fd://3`. If the +specified socket activated files aren't found, then Docker will exit. You +can find examples of using Systemd socket activation with Docker and +Systemd in the [Docker source tree]( +https://github.com/docker/docker/tree/master/contrib/init/systemd/). + +You can configure the Docker daemon to listen to multiple sockets at the same +time using multiple `-H` options: + + # listen using the default unix socket, and on 2 specific IP addresses on this host. + docker -d -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2 + +The Docker client will honor the `DOCKER_HOST` environment variable to set +the `-H` flag for the client. + + $ sudo docker -H tcp://0.0.0.0:2375 ps + # or + $ export DOCKER_HOST="tcp://0.0.0.0:2375" + $ sudo docker ps + # both are equal + +Setting the `DOCKER_TLS_VERIFY` environment variable to any value other than the empty +string is equivalent to setting the `--tlsverify` flag. The following are equivalent: + + $ sudo docker --tlsverify ps + # or + $ export DOCKER_TLS_VERIFY=1 + $ sudo docker ps + +### Daemon storage-driver option + +The Docker daemon has support for three different image layer storage drivers: `aufs`, +`devicemapper`, and `btrfs`. + +The `aufs` driver is the oldest, but is based on a Linux kernel patch-set that +is unlikely to be merged into the main kernel. These are also known to cause some +serious kernel crashes. However, `aufs` is also the only storage driver that allows +containers to share executable and shared library memory, so is a useful choice +when running thousands of containers with the same program or libraries. + +The `devicemapper` driver uses thin provisioning and Copy on Write (CoW) snapshots. +This driver will create a 100GB sparse file containing all your images and +containers. Each container will be limited to a 10 GB thin volume, and either of +these will require tuning - see [~jpetazzo/Resizing Docker containers with the +Device Mapper plugin]( http://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/) +To tell the Docker daemon to use `devicemapper`, use `docker -d -s devicemapper`. +The `btrfs` driver is very fast for `docker build` - but like `devicemapper` does not +share executable memory between devices. Use `docker -d -s btrfs -g /mnt/btrfs_partition`. + + +### Docker exec-driver option + +The Docker daemon uses a specifically built `libcontainer` execution driver as its +interface to the Linux kernel `namespaces`, `cgroups`, and `SELinux`. + +There is still legacy support for the original [LXC userspace tools]( +https://linuxcontainers.org/) via the `lxc` execution driver, however, this is +not where the primary development of new functionality is taking place. +Add `-e lxc` to the daemon flags to use the `lxc` execution driver. + + +### Daemon DNS options + To set the DNS server for all Docker containers, use `docker -d --dns 8.8.8.8`. To set the DNS search domain for all Docker containers, use `docker -d --dns-search example.com`. -To run the daemon with debug output, use `docker -d -D`. +### Miscellaneous options -To use lxc as the execution driver, use `docker -d -e lxc`. +IP masquerading uses address translation to allow containers without a public IP to talk +to other machines on the Internet. This may interfere with some network topologies and +can be disabled with --ip-masq=false. -The docker client will also honor the `DOCKER_HOST` environment variable to set -the `-H` flag for the client. - $ docker -H tcp://0.0.0.0:2375 ps - # or - $ export DOCKER_HOST="tcp://0.0.0.0:2375" - $ docker ps - # both are equal - -To run the daemon with [systemd socket activation]( -http://0pointer.de/blog/projects/socket-activation.html), use -`docker -d -H fd://`. Using `fd://` will work perfectly for most setups but -you can also specify individual sockets too `docker -d -H fd://3`. If the -specified socket activated files aren't found then docker will exit. You -can find examples of using systemd socket activation with docker and -systemd in the [docker source tree]( -https://github.com/docker/docker/blob/master/contrib/init/systemd/socket-activation/). Docker supports softlinks for the Docker data directory (`/var/lib/docker`) and for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be set like this: @@ -126,6 +203,7 @@ Docker supports softlinks for the Docker data directory export DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1 + ## attach Usage: docker attach [OPTIONS] CONTAINER @@ -135,23 +213,26 @@ Docker supports softlinks for the Docker data directory --no-stdin=false Do not attach STDIN --sig-proxy=true Proxy all received signals to the process (even in non-TTY mode). SIGCHLD, SIGKILL, and SIGSTOP are not proxied. -The `attach` command will allow you to view or -interact with any running container, detached (`-d`) -or interactive (`-i`). You can attach to the same -container at the same time - screen sharing style, or quickly view the -progress of your daemonized process. +The `attach` command lets you view or interact with any running container's +primary process (`pid 1`). + +You can attach to the same contained process multiple times simultaneously, screen +sharing style, or quickly view the progress of your daemonized process. + +> **Note:** This command is not for running a new process in a container. +> See: [`docker exec`](#exec). You can detach from the container again (and leave it running) with -`CTRL-C` (for a quiet exit) or `CTRL-\` -to get a stacktrace of the Docker client when it quits. When -you detach from the container's process the exit code will be returned -to the client. +`CTRL-p CTRL-q` (for a quiet exit), or `CTRL-c` which will send a +SIGKILL to the container, or `CTRL-\` to get a stacktrace of the +Docker client when it quits. When you detach from the container's +process the exit code will be returned to the client. To stop a container, use `docker stop`. To kill the container, use `docker kill`. -### Examples: +#### Examples $ ID=$(sudo docker run -d ubuntu /usr/bin/top -b) $ sudo docker attach $ID @@ -207,8 +288,8 @@ When a single Dockerfile is given as `URL` or is piped through `STDIN` (`docker build - < Dockerfile`), then no context is set. When a Git repository is set as `URL`, then the repository is used as -the context. The Git repository is cloned with its submodules (`git -clone -recursive`). A fresh `git clone` occurs in a temporary directory +the context. The Git repository is cloned with its submodules +(`git clone -recursive`). A fresh `git clone` occurs in a temporary directory on your local host, and then this is sent to the Docker daemon as the context. This way, your local user credentials and VPN's etc can be used to access private repositories. @@ -219,11 +300,33 @@ Exclusion patterns match files or directories relative to `PATH` that will be excluded from the context. Globbing is done using Go's [filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. +Please note that `.dockerignore` files in other subdirectories are +considered as normal files. Filepaths in .dockerignore are absolute with +the current directory as the root. Wildcards are allowed but the search +is not recursive. + +#### Example .dockerignore file + */temp* + */*/temp* + temp? + +The first line above `*/temp*`, would ignore all files with names starting with +`temp` from any subdirectory below the root directory. For example, a file named +`/somedir/temporary.txt` would be ignored. The second line `*/*/temp*`, will +ignore files starting with name `temp` from any subdirectory that is two levels +below the root directory. For example, the file `/somedir/subdir/temporary.txt` +would get ignored in this case. The last line in the above example `temp?` +will ignore the files that match the pattern from the root directory. +For example, the files `tempa`, `tempb` are ignored from the root directory. +Currently there is no support for regular expressions. Formats +like `[^temp*]` are ignored. + + See also: [*Dockerfile Reference*](/reference/builder). -### Examples: +#### Examples $ sudo docker build . Uploading context 10240 bytes @@ -270,7 +373,7 @@ If you wish to keep the intermediate containers after the build is complete, you must use `--rm=false`. This does not affect the build cache. - $ docker build . + $ sudo docker build . Uploading context 18.829 MB Uploading context Step 0 : FROM busybox @@ -280,7 +383,7 @@ affect the build cache. ---> 99cc1ad10469 Successfully built 99cc1ad10469 $ echo ".git" > .dockerignore - $ docker build . + $ sudo docker build . Uploading context 6.76 MB Uploading context Step 0 : FROM busybox @@ -348,15 +451,15 @@ while the image is committed. This reduces the likelihood of encountering data corruption during the process of creating the commit. If this behavior is undesired, set the 'p' option to false. -### Commit an existing container +#### Commit an existing container $ sudo docker ps ID IMAGE COMMAND CREATED STATUS PORTS c3f279d17e0a ubuntu:12.04 /bin/bash 7 days ago Up 25 hours 197387f1b436 ubuntu:12.04 /bin/bash 7 days ago Up 25 hours - $ docker commit c3f279d17e0a SvenDowideit/testimage:version3 + $ sudo docker commit c3f279d17e0a SvenDowideit/testimage:version3 f5283438590d - $ docker images | head + $ sudo docker images | head REPOSITORY TAG ID CREATED VIRTUAL SIZE SvenDowideit/testimage version3 f5283438590d 16 seconds ago 335.7 MB @@ -369,6 +472,68 @@ path. Paths are relative to the root of the filesystem. Copy files/folders from the PATH to the HOSTPATH +## create + +Creates a new container. + + Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...] + + Create a new container + + -a, --attach=[] Attach to STDIN, STDOUT or STDERR. + --add-host=[] Add a custom host-to-IP mapping (host:ip) + -c, --cpu-shares=0 CPU shares (relative weight) + --cap-add=[] Add Linux capabilities + --cap-drop=[] Drop Linux capabilities + --cidfile="" Write the container ID to the file + --cpuset="" CPUs in which to allow execution (0-3, 0,1) + --device=[] Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc) + --dns=[] Set custom DNS servers + --dns-search=[] Set custom DNS search domains + -e, --env=[] Set environment variables + --entrypoint="" Overwrite the default ENTRYPOINT of the image + --env-file=[] Read in a line delimited file of environment variables + --expose=[] Expose a port from the container without publishing it to your host + -h, --hostname="" Container host name + -i, --interactive=false Keep STDIN open even if not attached + --link=[] Add link to another container in the form of name:alias + --lxc-conf=[] (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" + -m, --memory="" Memory limit (format: , where unit = b, k, m or g) + --name="" Assign a name to the container + --net="bridge" Set the Network mode for the container + 'bridge': creates a new network stack for the container on the docker bridge + 'none': no networking for this container + 'container:': reuses another container network stack + 'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. + -P, --publish-all=false Publish all exposed ports to the host interfaces + -p, --publish=[] Publish a container's port to the host + format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort + (use 'docker port' to see the actual mapping) + --privileged=false Give extended privileges to this container + --restart="" Restart policy to apply when a container exits (no, on-failure[:max-retry], always) + -t, --tty=false Allocate a pseudo-TTY + -u, --user="" Username or UID + -v, --volume=[] Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container) + --volumes-from=[] Mount volumes from the specified container(s) + -w, --workdir="" Working directory inside the container + +The `docker create` command creates a writeable container layer over +the specified image and prepares it for running the specified command. +The container ID is then printed to `STDOUT`. +This is similar to `docker run -d` except the container is never started. +You can then use the `docker start ` command to start the +container at any point. + +This is useful when you want to set up a container configuration ahead +of time so that it is ready to start when you need it. + +#### Example + + $ sudo docker create -t -i fedora bash + 6d8af538ec541dd581ebc2a24153a28329acb5268abe5ef868c1f1a261221752 + $ sudo docker start -a -i 6d8af538ec5 + bash-4.2# + ## diff List the changed files and directories in a container᾿s filesystem @@ -408,7 +573,15 @@ For example: --since="" Show all events created since timestamp --until="" Stream events until this timestamp -### Examples +Docker containers will report the following events: + + create, destroy, die, export, kill, pause, restart, start, stop, unpause + +and Docker images will report: + + untag, delete + +#### Examples You'll need two shells for this example. @@ -442,6 +615,35 @@ You'll need two shells for this example. 2014-09-03T15:49:29.999999999Z07:00 4386fb97867d: (from 12de384bfb10) die 2014-09-03T15:49:29.999999999Z07:00 4386fb97867d: (from 12de384bfb10) stop +## exec + + Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...] + + Run a command in an existing container + + -d, --detach=false Detached mode: run command in the background + -i, --interactive=false Keep STDIN open even if not attached + -t, --tty=false Allocate a pseudo-TTY + +The `docker exec` command runs a new command in a running container. + +The `docker exec` command will typically be used after `docker run` or `docker start`. + +#### Examples + + $ sudo docker run --name ubuntu_bash --rm -i -t ubuntu bash + +This will create a container named `ubuntu_bash` and start a Bash session. + + $ sudo docker exec -d ubuntu_bash touch /tmp/execWorks + +This will create a new file `/tmp/execWorks` inside the running container +`ubuntu_bash`, in the background. + + $ sudo docker exec ubuntu_bash -it bash + +This will create a new Bash session in the container `ubuntu_bash`. + ## export Usage: docker export CONTAINER @@ -463,7 +665,7 @@ For example: To see how the `docker:latest` image was built: - $ docker history docker + $ sudo docker history docker IMAGE CREATED CREATED BY SIZE 3e23a5875458790b7a806f95f7ec0d0b2a5c1659bfc899c89f939f6d5b8f7094 8 days ago /bin/sh -c #(nop) ENV LC_ALL=C.UTF-8 0 B 8578938dd17054dce7993d21de79e96a037400e8d28e15e7290fea4f65128a36 8 days ago /bin/sh -c dpkg-reconfigure locales && locale-gen C.UTF-8 && /usr/sbin/update-locale LANG=C.UTF-8 1.245 MB @@ -491,7 +693,7 @@ decrease disk usage, and speed up `docker build` by allowing each step to be cached. These intermediate layers are not shown by default. -### Listing the most recently created images +#### Listing the most recently created images $ sudo docker images | head REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE @@ -505,7 +707,7 @@ by default. tryout latest 2629d1fa0b81 23 hours ago 131.5 MB 5ed6274db6ce 24 hours ago 1.089 GB -### Listing the full length image IDs +#### Listing the full length image IDs $ sudo docker images --no-trunc | head REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE @@ -519,7 +721,7 @@ by default. tryout latest 2629d1fa0b81b222fca63371ca16cbf6a0772d07759ff80e8d1369b926940074 23 hours ago 131.5 MB 5ed6274db6ceb2397844896966ea239290555e74ef307030ebb01ff91b1914df 24 hours ago 1.089 GB -### Filtering +#### Filtering The filtering flag (`-f` or `--filter`) format is of "key=value". If there are more than one filter, then pass multiple flags (e.g., `--filter "foo=bar" --filter "bif=baz"`) @@ -527,7 +729,7 @@ than one filter, then pass multiple flags (e.g., `--filter "foo=bar" --filter "b Current filters: * dangling (boolean - true or false) -#### untagged images +##### Untagged images $ sudo docker images --filter "dangling=true" @@ -541,7 +743,7 @@ Current filters: This will display untagged images, that are the leaves of the images tree (not intermediary layers). These images occur when a new build of an image takes the -repo:tag away from the IMAGE ID, leaving it untagged. A warning will be issued +`repo:tag` away from the image ID, leaving it untagged. A warning will be issued if trying to remove an image when a container is presently using it. By having this flag it allows for batch cleanup. @@ -558,7 +760,6 @@ Ready for use by `docker rmi ...`, like: NOTE: Docker will warn you if any containers exist that are using these untagged images. - ## import Usage: docker import URL|- [REPOSITORY[:TAG]] @@ -570,7 +771,7 @@ URLs must start with `http` and point to a single file archive (.tar, you would like to import from a local directory or archive, you can use the `-` parameter to take the data from `STDIN`. -### Examples +#### Examples **Import from a remote location:** @@ -618,14 +819,14 @@ For example: Username: svendowideit Registry: [https://index.docker.io/v1/] -The global `-D` option tells all `docker` comands to output debug information. +The global `-D` option tells all `docker` commands to output debug information. When sending issue reports, please use `docker version` and `docker -D info` to ensure we know how your setup is configured. ## inspect - Usage: docker inspect CONTAINER|IMAGE [CONTAINER|IMAGE...] + Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...] Return low-level information on a container or image @@ -637,9 +838,9 @@ specified, the given template will be executed for each result. Go's [text/template](http://golang.org/pkg/text/template/) package describes all the details of the format. -### Examples +#### Examples -**Get an instance'sIP Address:** +**Get an instance's IP address:** For the most part, you can pick out any field from the JSON in a fairly straightforward manner. @@ -655,25 +856,22 @@ output: **Find a Specific Port Mapping:** -The `.Field` syntax doesn't work when the field name -begins with a number, but the template language's `index` -function does. The `.NetworkSettings.Ports` -section contains a map of the internal port mappings to a list -of external address/port objects, so to grab just the numeric public -port, you use `index` to find the specific port map, -and then `index` 0 contains first object inside of -that. Then we ask for the `HostPort` field to get -the public address. +The `.Field` syntax doesn't work when the field name begins with a +number, but the template language's `index` function does. The +`.NetworkSettings.Ports` section contains a map of the internal port +mappings to a list of external address/port objects, so to grab just the +numeric public port, you use `index` to find the specific port map, and +then `index` 0 contains the first object inside of that. Then we ask for +the `HostPort` field to get the public address. $ sudo docker inspect --format='{{(index (index .NetworkSettings.Ports "8787/tcp") 0).HostPort}}' $INSTANCE_ID **Get config:** -The `.Field` syntax doesn't work when the field -contains JSON data, but the template language's custom `json` -function does. The `.config` section -contains complex json object, so to grab it as JSON, you use -`json` to convert config object into JSON +The `.Field` syntax doesn't work when the field contains JSON data, but +the template language's custom `json` function does. The `.config` +section contains complex JSON object, so to grab it as JSON, you use +`json` to convert the configuration object into JSON. $ sudo docker inspect --format='{{json .config}}' $INSTANCE_ID @@ -690,7 +888,7 @@ signal specified with option `--signal`. ## load - Usage: docker load + Usage: docker load [OPTIONS] Load an image from a tar archive on STDIN @@ -724,11 +922,11 @@ Restores both images and tags. -p, --password="" Password -u, --username="" Username -If you want to login to a self-hosted registry you can -specify this by adding the server name. +If you want to login to a self-hosted registry you can specify this by +adding the server name. example: - $ docker login localhost:8080 + $ sudo docker login localhost:8080 ## logout @@ -738,11 +936,11 @@ specify this by adding the server name. For example: - $ docker logout localhost:8080 + $ sudo docker logout localhost:8080 ## logs - Usage: docker logs CONTAINER + Usage: docker logs [OPTIONS] CONTAINER Fetch the logs of a container @@ -759,14 +957,31 @@ Passing a negative number or a non-integer to `--tail` is invalid and the value is set to `all` in that case. This behavior may change in the future. The `docker logs --timestamp` commands will add an RFC3339Nano -timestamp, for example `2014-05-10T17:42:14.999999999Z07:00`, to each -log entry. +timestamp, for example `2014-09-16T06:17:46.000000000Z`, to each +log entry. To ensure that the timestamps for are aligned the +nano-second part of the timestamp will be padded with zero when necessary. ## port - Usage: docker port CONTAINER PRIVATE_PORT + Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]] - Lookup the public-facing port that is NAT-ed to PRIVATE_PORT + List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT + +You can find out all the ports mapped by not specifying a `PRIVATE_PORT`, or +just a specific mapping: + + $ sudo docker ps test + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + b650456536c7 busybox:latest top 54 minutes ago Up 54 minutes 0.0.0.0:1234->9876/tcp, 0.0.0.0:4321->7890/tcp test + $ sudo docker port test + 7890/tcp -> 0.0.0.0:4321 + 9876/tcp -> 0.0.0.0:1234 + $ sudo docker port test 7890/tcp + 0.0.0.0:4321 + $ sudo docker port test 7890/udp + 2014/06/24 11:53:36 Error: No public port '7890/udp' published for test + $ sudo docker port test 7890 + 0.0.0.0:4321 ## pause @@ -780,9 +995,9 @@ used, which is observable by the process being suspended. With the cgroups freez the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. -See the [cgroups freezer documentation] -(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for -further details. +See the +[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) +for further details. ## ps @@ -794,6 +1009,7 @@ further details. --before="" Show only container created before Id or Name, include non-running ones. -f, --filter=[] Provide filter values. Valid filters: exited= - containers with exit code of + status=(restarting|running|paused|exited) -l, --latest=false Show only the latest created container, include non-running ones. -n=-1 Show n last created containers, include non-running ones. --no-trunc=false Don't truncate output @@ -803,7 +1019,7 @@ further details. Running `docker ps` showing 2 linked containers. - $ docker ps + $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4c01db0b339c ubuntu:12.04 bash 17 seconds ago Up 16 seconds webapp d7886598dbe2 crosbymichael/redis:latest /redis-server --dir 33 minutes ago Up 33 minutes 6379/tcp redis,webapp/db @@ -811,16 +1027,15 @@ Running `docker ps` showing 2 linked containers. `docker ps` will show only running containers by default. To see all containers: `docker ps -a` -### Filtering +#### Filtering -The filtering flag (-f or --filter) format is a "key=value" pair. If there is more +The filtering flag (`-f` or `--filter)` format is a `key=value` pair. If there is more than one filter, then pass multiple flags (e.g. `--filter "foo=bar" --filter "bif=baz"`) Current filters: * exited (int - the code of exited containers. Only useful with '--all') - -#### Successfully exited containers +##### Successfully exited containers $ sudo docker ps -a --filter 'exited=0' CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES @@ -832,10 +1047,12 @@ This shows all the containers that have exited with status of '0' ## pull - Usage: docker pull NAME[:TAG] + Usage: docker pull [OPTIONS] NAME[:TAG] Pull an image or a repository from the registry + -a, --all-tags=false Download all tagged images in the repository + Most of your images will be created on top of a base image from the [Docker Hub](https://hub.docker.com) registry. @@ -845,18 +1062,23 @@ can `pull` and try without needing to define and configure your own. It is also possible to manually specify the path of a registry to pull from. For example, if you have set up a local registry, you can specify its path to pull from it. A repository path is similar to a URL, but does not contain -a protocol specifier (https://, for example). +a protocol specifier (`https://`, for example). To download a particular image, or set of images (i.e., a repository), use `docker pull`: - $ docker pull debian - # will pull all the images in the debian repository - $ docker pull debian:testing - # will pull only the image named debian:testing and any intermediate layers - # it is based on. (Typically the empty `scratch` image, a MAINTAINERs layer, + $ sudo docker pull debian + # will pull the debian:latest image, its intermediate layers + # and any aliases of the same id + $ sudo docker pull debian:testing + # will pull the image named ubuntu:trusty, ubuntu:14.04 + # which is an alias of the same image + # and any intermediate layers it is based on. + # (Typically the empty `scratch` image, a MAINTAINER layer, # and the un-tarred base). - $ docker pull registry.hub.docker.com/debian + $ sudo docker pull --all-tags centos + # will pull all the images from the centos repository + $ sudo docker pull registry.hub.docker.com/debian # manually specifies the path to the default Docker registry. This could # be replaced with the path to a local registry to pull from another source. @@ -887,13 +1109,7 @@ registry or to a self-hosted one. -l, --link=false Remove the specified link and not the underlying container -v, --volumes=false Remove the volumes associated with the container -### Known Issues (rm) - -- [Issue 197](https://github.com/docker/docker/issues/197) indicates - that `docker kill` may leave directories behind - and make it difficult to remove the container. - -### Examples: +#### Examples $ sudo docker rm /redis /redis @@ -904,34 +1120,32 @@ This will remove the container referenced under the link $ sudo docker rm --link /webapp/redis /webapp/redis -This will remove the underlying link between `/webapp` -and the `/redis` containers removing all -network communication. +This will remove the underlying link between `/webapp` and the `/redis` +containers removing all network communication. $ sudo docker rm --force redis redis The main process inside the container referenced under the link `/redis` will receive -SIGKILL, then the container will be removed. +`SIGKILL`, then the container will be removed. - -This command will delete all stopped containers. The command -`docker ps -a -q` will return all existing container -IDs and pass them to the `rm` command which will -delete them. Any running containers will not be deleted. +This command will delete all stopped containers. The command `docker ps +-a -q` will return all existing container IDs and pass them to the `rm` +command which will delete them. Any running containers will not be +deleted. ## rmi - Usage: docker rmi IMAGE [IMAGE...] + Usage: docker rmi [OPTIONS] IMAGE [IMAGE...] Remove one or more images -f, --force=false Force removal of the image --no-prune=false Do not delete untagged parents -### Removing tagged images +#### Removing tagged images -Images can be removed either by their short or long ID`s, or their image +Images can be removed either by their short or long IDs, or their image names. If an image has more than one name, each of them needs to be removed before the image is removed. @@ -964,12 +1178,13 @@ removed before the image is removed. Run a command in a new container -a, --attach=[] Attach to STDIN, STDOUT or STDERR. + --add-host=[] Add a custom host-to-IP mapping (host:ip) -c, --cpu-shares=0 CPU shares (relative weight) --cap-add=[] Add Linux capabilities --cap-drop=[] Drop Linux capabilities --cidfile="" Write the container ID to the file --cpuset="" CPUs in which to allow execution (0-3, 0,1) - -d, --detach=false Detached mode: run container in the background and print new container ID + -d, --detach=false Detached mode: run the container in the background and print the new container ID --device=[] Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc) --dns=[] Set custom DNS servers --dns-search=[] Set custom DNS search domains @@ -990,10 +1205,10 @@ removed before the image is removed. 'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. -P, --publish-all=false Publish all exposed ports to the host interfaces -p, --publish=[] Publish a container's port to the host - format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort + format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort (use 'docker port' to see the actual mapping) --privileged=false Give extended privileges to this container - --restart="" Restart policy to apply when a container exits (no, on-failure, always) + --restart="" Restart policy to apply when a container exits (no, on-failure[:max-retry], always) --rm=false Automatically remove the container when it exits (incompatible with -d) --sig-proxy=true Proxy received signals to the process (even in non-TTY mode). SIGCHLD, SIGSTOP, and SIGKILL are not proxied. -t, --tty=false Allocate a pseudo-TTY @@ -1016,14 +1231,7 @@ See the [Docker User Guide](/userguide/dockerlinks/) for more detailed information about the `--expose`, `-p`, `-P` and `--link` parameters, and linking containers. -### Known Issues (run –volumes-from) - -- [Issue 2702](https://github.com/docker/docker/issues/2702): - "lxc-start: Permission denied - failed to mount" could indicate a - permissions problem with AppArmor. Please see the issue for a - workaround. - -### Examples: +#### Examples $ sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test" @@ -1074,9 +1282,9 @@ folder before starting your container. $ sudo docker run -t -i -v /var/run/docker.sock:/var/run/docker.sock -v ./static-docker:/usr/bin/docker busybox sh By bind-mounting the docker unix socket and statically linked docker -binary (such as that provided by [https://get.docker.io]( -https://get.docker.io)), you give the container the full access to create and -manipulate the host's docker daemon. +binary (such as that provided by [https://get.docker.com]( +https://get.docker.com)), you give the container the full access to create and +manipulate the host's Docker daemon. $ sudo docker run -p 127.0.0.1:80:8080 ubuntu bash @@ -1096,7 +1304,7 @@ ports in Docker. This sets environmental variables in the container. For illustration all three flags are shown here. Where `-e`, `--env` take an environment variable and value, or if no "=" is provided, then that variable's current value is passed -through (i.e. $MYVAR1 from the host is set to $MYVAR1 in the container). All +through (i.e. `$MYVAR1` from the host is set to `$MYVAR1` in the container). All three flags, `-e`, `--env` and `--env-file` can be repeated. Regardless of the order of these three flags, the `--env-file` are processed @@ -1109,7 +1317,7 @@ override variables as needed. TEST_FOO=This is a test The `--env-file` flag takes a filename as an argument and expects each line -to be in the VAR=VAL format, mimicking the argument passed to `--env`. Comment +to be in the `VAR=VAL` format, mimicking the argument passed to `--env`. Comment lines need only be prefixed with `#` An example of a file passed with `--env-file` @@ -1181,14 +1389,13 @@ retrieve the container's ID once the container has finished running. brw-rw---- 1 root disk 8, 3 Feb 9 16:05 /dev/sdd crw-rw-rw- 1 root root 1, 5 Feb 9 16:05 /dev/nulo -It is often necessary to directly expose devices to a container. ``--device`` +It is often necessary to directly expose devices to a container. The `--device` option enables that. For example, a specific block storage device or loop device or audio device can be added to an otherwise unprivileged container -(without the ``--privileged`` flag) and have the application directly access it. +(without the `--privileged` flag) and have the application directly access it. -** Security note: ** - -``--device`` cannot be safely used with ephemeral devices. Block devices that may be removed should not be added to untrusted containers with ``--device``! +**Note:** +> `--device` cannot be safely used with ephemeral devices. Block devices that may be removed should not be added to untrusted containers with `--device`. **A complete example:** @@ -1198,7 +1405,7 @@ device or audio device can be added to an otherwise unprivileged container $ sudo docker run -d -p 1443:443 --dns=10.0.0.1 --dns-search=dev.org -v /var/log/httpd --volumes-from static --link riak --link app -h www.sven.dev.org --name web webserver $ sudo docker run -t -i --rm --volumes-from web -w /var/log/httpd busybox tail -f access.log -This example shows 5 containers that might be set up to test a web +This example shows five containers that might be set up to test a web application change: 1. Start a pre-prepared volume image `static-web-files` (in the background) @@ -1230,10 +1437,11 @@ how a container should or should not be restarted on exit. ** on-failure ** - Restart the container only if it exits with a non zero exit status. -** always ** - Always restart the container reguardless of the exit status. +** always ** - Always restart the container regardless of the exit status. -You can also specify the maximum amount of times Docker will try to restart the -container when using the ** on-failure ** policy. The default is that Docker will try forever to restart the container. +You can also specify the maximum amount of times Docker will try to +restart the container when using the ** on-failure ** policy. The +default is that Docker will try forever to restart the container. $ sudo docker run --restart=always redis @@ -1242,23 +1450,25 @@ the container exits, Docker will restart it. $ sudo docker run --restart=on-failure:10 redis -This will run the `redis` container with a restart policy of ** on-failure ** and a -maximum restart count of 10. If the `redis` container exits with a non-zero exit -status more than 10 times in a row Docker will abort trying to restart the container. +This will run the `redis` container with a restart policy of ** +on-failure ** and a maximum restart count of 10. If the `redis` +container exits with a non-zero exit status more than 10 times in a row +Docker will abort trying to restart the container. Providing a maximum +restart limit is only valid for the ** on-failure ** policy. ## save - Usage: docker save IMAGE + Usage: docker save [OPTIONS] IMAGE [IMAGE...] - Save an image to a tar archive (streamed to STDOUT by default) + Save an image(s) to a tar archive (streamed to STDOUT by default) - -o, --output="" Write to an file, instead of STDOUT + -o, --output="" Write to a file, instead of STDOUT -Produces a tarred repository to the standard output stream. Contains all -parent layers, and all tags + versions, or specified repo:tag. +Produces a tarred repository to the standard output stream. +Contains all parent layers, and all tags + versions, or specified `repo:tag`, for +each argument provided. -It is used to create a backup that can then be used with -`docker load` +It is used to create a backup that can then be used with `docker load` $ sudo docker save busybox > busybox.tar $ ls -sh busybox.tar @@ -1269,11 +1479,15 @@ It is used to create a backup that can then be used with $ sudo docker save -o fedora-all.tar fedora $ sudo docker save -o fedora-latest.tar fedora:latest +It is even useful to cherry-pick particular tags of an image repository + + $ sudo docker save -o ubuntu.tar ubuntu:lucid ubuntu:saucy + ## search Search [Docker Hub](https://hub.docker.com) for images - Usage: docker search TERM + Usage: docker search [OPTIONS] TERM Search the Docker Hub for images @@ -1287,12 +1501,12 @@ more details on finding shared images from the command line. ## start - Usage: docker start CONTAINER [CONTAINER...] + Usage: docker start [OPTIONS] CONTAINER [CONTAINER...] Restart a stopped container - -a, --attach=false Attach container's STDOUT and STDERR and forward all signals to the process - -i, --interactive=false Attach container's STDIN + -a, --attach=false Attach container's `STDOUT` and `STDERR` and forward all signals to the process + -i, --interactive=false Attach container's `STDIN` When run on a container that has already been started, takes no action and succeeds unconditionally. @@ -1301,12 +1515,12 @@ takes no action and succeeds unconditionally. Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] - Stop a running container by sending SIGTERM and then SIGKILL after a grace period + Stop a running container by sending `SIGTERM` and then `SIGKILL` after a grace period -t, --time=10 Number of seconds to wait for the container to stop before killing it. Default is 10 seconds. -The main process inside the container will receive SIGTERM, and after a -grace period, SIGKILL +The main process inside the container will receive `SIGTERM`, and after a +grace period, `SIGKILL`. ## tag @@ -1335,9 +1549,9 @@ them to [*Share Images via Repositories*]( The `docker unpause` command uses the cgroups freezer to un-suspend all processes in a container. -See the [cgroups freezer documentation] -(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for -further details. +See the +[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) +for further details. ## version diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index a933a32bea..67007ccff7 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -18,7 +18,7 @@ other `docker` command. The basic `docker run` command takes this form: - $ docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] + $ sudo docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] To learn how to interpret the types of `[OPTIONS]`, see [*Option types*](/reference/commandline/cli/#option-types). @@ -91,7 +91,7 @@ streams]( https://github.com/docker/docker/blob/ specify to which of the three standard streams (`STDIN`, `STDOUT`, `STDERR`) you'd like to connect instead, as in: - $ docker run -a stdin -a stdout -i -t ubuntu /bin/bash + $ sudo docker run -a stdin -a stdout -i -t ubuntu /bin/bash For interactive processes (like a shell) you will typically want a tty as well as persistent standard input (`STDIN`), so you'll use `-i -t` @@ -139,6 +139,7 @@ example, `docker run ubuntu:14.04`. 'none': no networking for this container 'container:': reuses another container network stack 'host': use the host network stack inside the container + --add-host="" : Add a line to /etc/hosts (host:IP) By default, all containers have networking enabled and they can make any outgoing connections. The operator can completely disable networking @@ -192,9 +193,25 @@ Example running a Redis container with Redis binding to `localhost` then running the `redis-cli` command and connecting to the Redis server over the `localhost` interface. - $ docker run -d --name redis example/redis --bind 127.0.0.1 + $ sudo docker run -d --name redis example/redis --bind 127.0.0.1 $ # use the redis container's network stack to access localhost - $ docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1 + $ sudo docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1 + +### Managing /etc/hosts + +Your container will have lines in `/etc/hosts` which define the hostname of the +container itself as well as `localhost` and a few other common things. The +`--add-host` flag can be used to add additional lines to `/etc/hosts`. + + $ /docker run -ti --add-host db-static:86.75.30.9 ubuntu cat /etc/hosts + 172.17.0.22 09d03f76bf2c + fe00::0 ip6-localnet + ff00::0 ip6-mcastprefix + ff02::1 ip6-allnodes + ff02::2 ip6-allrouters + 127.0.0.1 localhost + ::1 localhost ip6-localhost ip6-loopback + 86.75.30.9 db-static ## Clean Up (–-rm) @@ -208,6 +225,42 @@ the container exits**, you can add the `--rm` flag: --rm=false: Automatically remove the container when it exits (incompatible with -d) +## Security Configuration + --security-opt="label:user:USER" : Set the label user for the container + --security-opt="label:role:ROLE" : Set the label role for the container + --security-opt="label:type:TYPE" : Set the label type for the container + --security-opt="label:level:LEVEL" : Set the label level for the container + --security-opt="label:disable" : Turn off label confinement for the container + --secutity-opt="apparmor:PROFILE" : Set the apparmor profile to be applied + to the container + +You can override the default labeling scheme for each container by specifying +the `--security-opt` flag. For example, you can specify the MCS/MLS level, a +requirement for MLS systems. Specifying the level in the following command +allows you to share the same content between containers. + + # docker run --security-opt label:level:s0:c100,c200 -i -t fedora bash + +An MLS example might be: + + # docker run --security-opt label:level:TopSecret -i -t rhel7 bash + +To disable the security labeling for this container versus running with the +`--permissive` flag, use the following command: + + # docker run --security-opt label:disable -i -t fedora bash + +If you want a tighter security policy on the processes within a container, +you can specify an alternate type for the container. You could run a container +that is only allowed to listen on Apache ports by executing the following +command: + + # docker run --security-opt label:type:svirt_apache_t -i -t centos bash + +Note: + +You would have to write policy defining a `svirt_apache_t` type. + ## Runtime Constraints on CPU and Memory The operator can also adjust the performance parameters of the @@ -231,6 +284,7 @@ them via Docker. --cap-add: Add Linux capabilities --cap-drop: Drop Linux capabilities --privileged=false: Give extended privileges to this container + --device=[]: Allows you to run devices inside the container without the --privileged flag. --lxc-conf=[]: (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" By default, Docker containers are "unprivileged" and cannot, for @@ -243,17 +297,23 @@ https://www.kernel.org/doc/Documentation/cgroups/devices.txt)). When the operator executes `docker run --privileged`, Docker will enable to access to all devices on the host as well as set some configuration -in AppArmor to allow the container nearly all the same access to the +in AppArmor or SELinux to allow the container nearly all the same access to the host as processes running outside containers on the host. Additional information about running with `--privileged` is available on the [Docker Blog](http://blog.docker.com/2013/09/docker-can-now-run-within-docker/). +If you want to limit access to a specific device or devices you can use +the `--device` flag. It allows you to specify one or more devices that +will be accessible within the container. + + $ sudo docker run --device=/dev/snd:/dev/snd ... + In addition to `--privileged`, the operator can have fine grain control over the capabilities using `--cap-add` and `--cap-drop`. By default, Docker has a default list of capabilities that are kept. Both flags support the value `all`, so if the operator wants to have all capabilities but `MKNOD` they could use: - $ docker run --cap-add=ALL --cap-drop=MKNOD ... + $ sudo docker run --cap-add=ALL --cap-drop=MKNOD ... For interacting with the network stack, instead of using `--privileged` they should use `--cap-add=NET_ADMIN` to modify the network interfaces. @@ -292,7 +352,7 @@ Dockerfile instruction and how the operator can override that setting. Recall the optional `COMMAND` in the Docker commandline: - $ docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] + $ sudo docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] This command is optional because the person who created the `IMAGE` may have already provided a default `COMMAND` using the Dockerfile `CMD` @@ -319,12 +379,12 @@ runtime by using a string to specify the new `ENTRYPOINT`. Here is an example of how to run a shell in a container that has been set up to automatically run something else (like `/usr/bin/redis-server`): - $ docker run -i -t --entrypoint /bin/bash example/redis + $ sudo docker run -i -t --entrypoint /bin/bash example/redis or two examples of how to pass more parameters to that ENTRYPOINT: - $ docker run -i -t --entrypoint /bin/bash example/redis -c ls -l - $ docker run -i -t --entrypoint /usr/bin/redis-cli example/redis --help + $ sudo docker run -i -t --entrypoint /bin/bash example/redis -c ls -l + $ sudo docker run -i -t --entrypoint /usr/bin/redis-cli example/redis --help ## EXPOSE (Incoming Ports) @@ -338,7 +398,7 @@ or override the Dockerfile's exposed defaults: -P=false : Publish all exposed ports to the host interfaces -p=[] : Publish a container᾿s port to the host (format: ip:hostPort:containerPort | ip::containerPort | - hostPort:containerPort) + hostPort:containerPort | containerPort) (use 'docker port' to see the actual mapping) --link="" : Add link to another container (name:alias) @@ -357,8 +417,9 @@ with `-P` or `-p,` or start the client container with `--link`. If the operator uses `-P` or `-p` then Docker will make the exposed port accessible on the host and the ports will be available to any client -that can reach the host. To find the map between the host ports and the -exposed ports, use `docker port`) +that can reach the host. When using `-P`, Docker will bind the exposed +ports to a random port on the host between 49153 and 65535. To find the +mapping between the host ports and the exposed ports, use `docker port`. If the operator uses `--link` when starting the new client container, then the client container can access the exposed port via a private @@ -367,11 +428,50 @@ client container to help indicate which interface and port to use. ## ENV (Environment Variables) -The operator can **set any environment variable** in the container by -using one or more `-e` flags, even overriding those already defined by -the developer with a Dockerfile `ENV`: +When a new container is created, Docker will set the following environment +variables automatically: - $ docker run -e "deep=purple" --rm ubuntu /bin/bash -c export + + + + + + + + + + + + + + + + + + + + +
Variable Value
HOME + Set based on the value of USER +
HOSTNAME + The hostname associated with the container +
PATH + Includes popular directories, such as :
+ /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +
TERM + xterm if the container is allocated a psuedo-TTY +
+ +The container may also include environment variables defined +as a result of the container being linked with another container. See +the [*Container Links*](/userguide/dockerlinks/#container-linking) +section for more details. + +Additionally, the operator can **set any environment variable** in the +container by using one or more `-e` flags, even overriding those mentioned +above, or already defined by the developer with a Dockerfile `ENV`: + + $ sudo docker run -e "deep=purple" --rm ubuntu /bin/bash -c export declare -x HOME="/" declare -x HOSTNAME="85bc26a0e200" declare -x OLDPWD @@ -389,23 +489,23 @@ information for connecting to the service container. Let's imagine we have a container running Redis: # Start the service container, named redis-name - $ docker run -d --name redis-name dockerfiles/redis + $ sudo docker run -d --name redis-name dockerfiles/redis 4241164edf6f5aca5b0e9e4c9eccd899b0b8080c64c0cd26efe02166c73208f3 # The redis-name container exposed port 6379 - $ docker ps + $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4241164edf6f $ dockerfiles/redis:latest /redis-stable/src/re 5 seconds ago Up 4 seconds 6379/tcp redis-name # Note that there are no public ports exposed since we didn᾿t use -p or -P - $ docker port 4241164edf6f 6379 + $ sudo docker port 4241164edf6f 6379 2014/01/25 00:55:38 Error: No public port '6379' published for 4241164edf6f Yet we can get information about the Redis container's exposed ports with `--link`. Choose an alias that will form a valid environment variable! - $ docker run --rm --link redis-name:redis_alias --entrypoint /bin/bash dockerfiles/redis -c export + $ sudo docker run --rm --link redis-name:redis_alias --entrypoint /bin/bash dockerfiles/redis -c export declare -x HOME="/" declare -x HOSTNAME="acda7f7b1cdc" declare -x OLDPWD @@ -422,15 +522,18 @@ valid environment variable! And we can use that information to connect from another container as a client: - $ docker run -i -t --rm --link redis-name:redis_alias --entrypoint /bin/bash dockerfiles/redis -c '/redis-stable/src/redis-cli -h $REDIS_ALIAS_PORT_6379_TCP_ADDR -p $REDIS_ALIAS_PORT_6379_TCP_PORT' + $ sudo docker run -i -t --rm --link redis-name:redis_alias --entrypoint /bin/bash dockerfiles/redis -c '/redis-stable/src/redis-cli -h $REDIS_ALIAS_PORT_6379_TCP_ADDR -p $REDIS_ALIAS_PORT_6379_TCP_PORT' 172.17.0.32:6379> Docker will also map the private IP address to the alias of a linked container by inserting an entry into `/etc/hosts`. You can use this mechanism to communicate with a linked container by its alias: - $ docker run -d --name servicename busybox sleep 30 - $ docker run -i -t --link servicename:servicealias busybox ping -c 1 servicealias + $ sudo docker run -d --name servicename busybox sleep 30 + $ sudo docker run -i -t --link servicename:servicealias busybox ping -c 1 servicealias + +If you restart the source container (`servicename` in this case), the recipient +container's `/etc/hosts` entry will be automatically updated. ## VOLUME (Shared Filesystems) diff --git a/docs/sources/robots.txt b/docs/sources/robots.txt deleted file mode 100644 index c2a49f4fb8..0000000000 --- a/docs/sources/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Allow: / diff --git a/docs/sources/static_files/docker_pull_chart.png b/docs/sources/static_files/docker_pull_chart.png index 73a145239c..57d3f68dbd 100644 Binary files a/docs/sources/static_files/docker_pull_chart.png and b/docs/sources/static_files/docker_pull_chart.png differ diff --git a/docs/sources/static_files/docker_push_chart.png b/docs/sources/static_files/docker_push_chart.png index 6486355e91..34b37073db 100644 Binary files a/docs/sources/static_files/docker_push_chart.png and b/docs/sources/static_files/docker_push_chart.png differ diff --git a/docs/sources/static_files/dockerlogo-v.png b/docs/sources/static_files/dockerlogo-v.png index 591e770d27..69ae685172 100644 Binary files a/docs/sources/static_files/dockerlogo-v.png and b/docs/sources/static_files/dockerlogo-v.png differ diff --git a/docs/sources/terms/images/docker-filesystems-busyboxrw.png b/docs/sources/terms/images/docker-filesystems-busyboxrw.png index 9ff8487b89..5f046cbc0a 100644 Binary files a/docs/sources/terms/images/docker-filesystems-busyboxrw.png and b/docs/sources/terms/images/docker-filesystems-busyboxrw.png differ diff --git a/docs/sources/terms/images/docker-filesystems-debian.png b/docs/sources/terms/images/docker-filesystems-debian.png index 61e5ddb2e3..3502b38460 100644 Binary files a/docs/sources/terms/images/docker-filesystems-debian.png and b/docs/sources/terms/images/docker-filesystems-debian.png differ diff --git a/docs/sources/terms/images/docker-filesystems-debianrw.png b/docs/sources/terms/images/docker-filesystems-debianrw.png index cacba4947b..387c9eb763 100644 Binary files a/docs/sources/terms/images/docker-filesystems-debianrw.png and b/docs/sources/terms/images/docker-filesystems-debianrw.png differ diff --git a/docs/sources/terms/images/docker-filesystems-generic.png b/docs/sources/terms/images/docker-filesystems-generic.png index ae54b72e88..e24dce635b 100644 Binary files a/docs/sources/terms/images/docker-filesystems-generic.png and b/docs/sources/terms/images/docker-filesystems-generic.png differ diff --git a/docs/sources/terms/images/docker-filesystems-multilayer.png b/docs/sources/terms/images/docker-filesystems-multilayer.png index daedebe9c1..632aa41cca 100644 Binary files a/docs/sources/terms/images/docker-filesystems-multilayer.png and b/docs/sources/terms/images/docker-filesystems-multilayer.png differ diff --git a/docs/sources/terms/images/docker-filesystems-multiroot.png b/docs/sources/terms/images/docker-filesystems-multiroot.png index 65b61d94f1..f9bcc38ff9 100644 Binary files a/docs/sources/terms/images/docker-filesystems-multiroot.png and b/docs/sources/terms/images/docker-filesystems-multiroot.png differ diff --git a/docs/sources/userguide/dockerimages.md b/docs/sources/userguide/dockerimages.md index e6583a0f82..a0a30408c6 100644 --- a/docs/sources/userguide/dockerimages.md +++ b/docs/sources/userguide/dockerimages.md @@ -93,6 +93,8 @@ download the `centos` image. ef52fb1fe610: Download complete . . . + Status: Downloaded newer image for centos + We can see that each layer of the image has been pulled down and now we can run a container from this image and we won't have to wait to download the image. @@ -166,6 +168,8 @@ update and create images. 1. We can update a container created from an image and commit the results to an image. 2. We can use a `Dockerfile` to specify instructions to create an image. +To learn more, check out the [Dockerfile tutorial](/userguide/level1). + ### Updating and committing an image To update an image we first need to create a container from the image @@ -468,11 +472,17 @@ We can then create a container from our new image. root@8196968dac35:/# > **Note:** -> This is just the briefest introduction to creating images. We've +> This is just a brief introduction to creating images. We've > skipped a whole bunch of other instructions that you can use. We'll see more of > those instructions in later sections of the Guide or you can refer to the > [`Dockerfile`](/reference/builder/) reference for a > detailed description and examples of every instruction. +> To help you write a clear, readable, maintainable `Dockerfile`, we've also +> written a [`Dockerfile` Best Practices guide](/articles/dockerfile_best-practices). + +### More + +To learn more, check out the [Dockerfile tutorial](/userguide/level1). ## Setting tags on an image @@ -531,3 +541,4 @@ by linking together multiple Docker containers. Go to [Linking Containers Together](/userguide/dockerlinks). + diff --git a/docs/sources/userguide/dockerizing.md b/docs/sources/userguide/dockerizing.md index 02ac90306b..9da4890bfa 100644 --- a/docs/sources/userguide/dockerizing.md +++ b/docs/sources/userguide/dockerizing.md @@ -81,7 +81,7 @@ of the root directory which shows us what looks like a typical Linux file system. You can play around inside this container and when you're done you can -use the `exit` command to finish. +use the `exit` command or enter Ctrl-D to finish. root@af8bae53bdd3:/# exit diff --git a/docs/sources/userguide/dockerlinks.md b/docs/sources/userguide/dockerlinks.md index 3624bf72c3..ce14bfa12a 100644 --- a/docs/sources/userguide/dockerlinks.md +++ b/docs/sources/userguide/dockerlinks.md @@ -26,8 +26,8 @@ container that ran a Python Flask application: > information on Docker networking [here](/articles/networking/). When that container was created, the `-P` flag was used to automatically map any -network ports inside it to a random high port from the range 49000 -to 49900 on our Docker host. Next, when `docker ps` was run, you saw that +network ports inside it to a random high port from the range 49153 +to 65535 on our Docker host. Next, when `docker ps` was run, you saw that port 5000 in the container was bound to port 49155 on the host. $ sudo docker ps nostalgic_morse @@ -66,7 +66,7 @@ current port bindings. This is also useful for showing you specific port configurations. For example, if you've bound the container port to the `localhost` on the host machine, then the `docker port` output will reflect that. - $ docker port nostalgic_morse 5000 + $ sudo docker port nostalgic_morse 5000 127.0.0.1:49155 > **Note:** @@ -137,7 +137,7 @@ image, which contains a PostgreSQL database. Now, you need to delete the `web` container you created previously so you can replace it with a linked one: - $ docker rm -f web + $ sudo docker rm -f web Now, create a new `web` container and link it with your `db` container. @@ -151,12 +151,13 @@ earlier. The `--link` flag takes the form: Where `name` is the name of the container we're linking to and `alias` is an alias for the link name. You'll see how that alias gets used shortly. -Next, look at your linked containers using `docker ps`. +Next, look at the names of your linked containers by filtering the full output of +`docker ps` to the last column (NAMES) using `docker ps --no-trunc | awk '{print $NF}'`. - $ docker ps - CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES - 349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db - aed84ee21bde training/webapp:latest python app.py 16 hours ago Up 2 minutes 0.0.0.0:49154->5000/tcp web + $ sudo docker ps --no-trunc | awk '{print $NF}' + NAMES + db, web/db + web You can see your named containers, `db` and `web`, and you can see that the `db` container also shows `web/db` in the `NAMES` column. This tells you that the @@ -178,7 +179,45 @@ recipient container in two ways: * Environment variables, * Updating the `/etc/hosts` file. -Docker can set a number of environment variables. You run the `env` +### Environment Variables + +When two containers are linked, Docker will set some environment variables +in the target container to enable programmatic discovery of information +related to the source container. + +First, Docker will set an `_NAME` environment variable specifying the +alias of each target container that was given in a `--link` parameter. So, +for example, if a new container called `web` is being linked to a database +container called `db` via `--link db:webdb` then in the `web` container +would be `WEBDB_NAME=/web/webdb`. + +Docker will then also define a set of environment variables for each +port that is exposed by the source container. The pattern followed is: + +* `_PORT__` will contain a URL reference to the +port. Where `` is the alias name specified in the `--link` parameter +(e.g. `webdb`), `` is the port number being exposed, and `` +is either `TCP` or `UDP`. The format of the URL will be: +`://:` +(e.g. `tcp://172.17.0.82:8080`). This URL will then be +split into the following 3 environment variables for convinience: +* `_PORT___ADDR` will contain just the IP address +from the URL (e.g. `WEBDB_PORT_8080_TCP_ADDR=172.17.0.82`). +* `_PORT___PORT` will contain just the port number +from the URL (e.g. `WEBDB_PORT_8080_TCP_PORT=8080`). +* `_PORT___PROTO` will contain just the protocol +from the URL (e.g. `WEBDB_PORT_8080_TCP_PROTO=tcp`). + +If there are multiple ports exposed then the above set of environment +variables will be defined for each one. + +Finally, there will be an environment variable called `_PORT` that will +contain the URL of the first exposed port of the source container. +For example, `WEBDB_PORT=tcp://172.17.0.82:8080`. In this case, 'first' +is defined as the lowest numbered port that is exposed. If that port is +used for both tcp and udp, then the tcp one will be specified. + +Returning back to our database example, you can run the `env` command to list the specified container's environment variables. ``` @@ -186,10 +225,10 @@ command to list the specified container's environment variables. . . . DB_NAME=/web2/db DB_PORT=tcp://172.17.0.5:5432 - DB_PORT_5000_TCP=tcp://172.17.0.5:5432 - DB_PORT_5000_TCP_PROTO=tcp - DB_PORT_5000_TCP_PORT=5432 - DB_PORT_5000_TCP_ADDR=172.17.0.5 + DB_PORT_5432_TCP=tcp://172.17.0.5:5432 + DB_PORT_5432_TCP_PROTO=tcp + DB_PORT_5432_TCP_PORT=5432 + DB_PORT_5432_TCP_ADDR=172.17.0.5 . . . ``` @@ -206,6 +245,8 @@ environment variables to configure your applications to connect to the database on the `db` container. The connection will be secure and private; only the linked `web` container will be able to talk to the `db` container. +### Updating the `/etc/hosts` file + In addition to the environment variables, Docker adds a host entry for the source container to the `/etc/hosts` file. Here's an entry for the `web` container: @@ -241,6 +282,16 @@ to make use of your `db` container. > example, you could have multiple (differently named) web containers attached to your >`db` container. +If you restart the source container, the linked containers `/etc/hosts` files +will be automatically updated with the source container's new IP address, +allowing linked communication to continue. + + $ sudo docker restart db + root@aed84ee21bde:/opt/webapp# cat /etc/hosts + 172.17.0.7 aed84ee21bde + . . . + 172.17.0.9 db + # Next step Now that you know how to link Docker containers together, the next step is diff --git a/docs/sources/userguide/dockerrepos.md b/docs/sources/userguide/dockerrepos.md index a73c4b7834..967ed0d8cf 100644 --- a/docs/sources/userguide/dockerrepos.md +++ b/docs/sources/userguide/dockerrepos.md @@ -67,6 +67,8 @@ Once you've found the image you want, you can download it with `docker pull Back + +# Dockerfile Tutorial + +## Test your Dockerfile knowledge - Level 1 + +### Questions + +
+ What is the Dockerfile instruction to specify the base image ?
+ + +
+ What is the Dockerfile instruction to execute any commands on the current image and commit the results?
+ + +
+ What is the Dockerfile instruction to specify the maintainer of the Dockerfile?
+ + +
+ What is the character used to add comment in Dockerfiles?
+ + +

+

+ + +

+ +
+ +### Fill the Dockerfile +Your best friend Eric Bardin sent you a Dockerfile, but some parts were lost in the ocean. Can you find the missing parts? +
+
+# This is a Dockerfile to create an image with Memcached and Emacs installed. 
+# VERSION 1.0
+# use the ubuntu base image provided by dotCloud + ub
+ E B, eric.bardin@dotcloud.com
+# make sure the package repository is up to date + echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list + apt-get update
+# install memcached +RUN apt-get install -y
+# install emacs + apt-get install -y emacs23 +
+
+ + + +
+

+ +## What's next? + +

In the next level, we will go into more detail about how to specify which command should be executed when the container starts, +which user to use, and how expose a particular port.

+ +Back +Go to the next level \ No newline at end of file diff --git a/docs/sources/userguide/level2.md b/docs/sources/userguide/level2.md new file mode 100644 index 0000000000..c4f2a2802c --- /dev/null +++ b/docs/sources/userguide/level2.md @@ -0,0 +1,97 @@ +page_title: Docker Images Test +page_description: How to work with Docker images. +page_keywords: documentation, docs, the docker guide, docker guide, docker, docker platform, virtualization framework, docker.io, Docker images, Docker image, image management, Docker repos, Docker repositories, docker, docker tag, docker tags, Docker Hub, collaboration + +Back + +#Dockerfile Tutorial + +## Test your Dockerfile knowledge - Level 2 + +### Questions: + +
+What is the Dockerfile instruction to specify the base image?
+ +
+ Which Dockerfile instruction sets the default command for your image?
+ +
+ What is the character used to add comments in Dockerfiles?
+ +
+ Which Dockerfile instruction sets the username to use when running the image?
+ +
+ What is the Dockerfile instruction to execute any command on the current image and commit the results?
+ +
+ Which Dockerfile instruction sets ports to be exposed when running the image?
+ +
+ What is the Dockerfile instruction to specify the maintainer of the Dockerfile?
+ +
+ Which Dockerfile instruction lets you trigger a command as soon as the container starts?
+ +
+

+ +

+ + +

+ +
+ +### Fill the Dockerfile +
+Your best friend Roberto Hashioka sent you a Dockerfile, but some parts were lost in the ocean. Can you find the missing parts? +
+
+# Redis
+#
+# VERSION       0.42
+#
+# use the ubuntu base image provided by dotCloud
+  ub
+MAINT Ro Ha roberto.hashioka@dotcloud.com
+# make sure the package repository is up to date + echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list + apt-get update
+# install wget (required for redis installation) + apt-get install -y wget
+# install make (required for redis installation) + apt-get install -y make
+# install gcc (required for redis installation) +RUN apt-get install -y
+# install apache2 + wget http://download.redis.io/redis-stable.tar.gz +tar xvzf redis-stable.tar.gz +cd redis-stable && make && make install
+# launch redis when starting the image + ["redis-server"]
+# run as user dameon + daemon
+# expose port 6379 + 6379 +
+ + +
+

+
+ +## What's next? +

+Thanks for going through our tutorial! We will be posting Level 3 shortly. Follow us on twitter
+ + +

+

In the meantime, check out this blog post by Michael Crosby that describes Dockerfile Best Practices.

+Back to the Docs! \ No newline at end of file diff --git a/docs/sources/userguide/login-web.png b/docs/sources/userguide/login-web.png index e9d26b5e80..353705ba5e 100644 Binary files a/docs/sources/userguide/login-web.png and b/docs/sources/userguide/login-web.png differ diff --git a/docs/sources/userguide/register-web.png b/docs/sources/userguide/register-web.png index 6c549f8fd3..709badc6d5 100644 Binary files a/docs/sources/userguide/register-web.png and b/docs/sources/userguide/register-web.png differ diff --git a/docs/sources/userguide/search.png b/docs/sources/userguide/search.png index ded0d0d2d3..187b8d9ed8 100644 Binary files a/docs/sources/userguide/search.png and b/docs/sources/userguide/search.png differ diff --git a/docs/sources/userguide/usingdocker.md b/docs/sources/userguide/usingdocker.md index a882a79c7d..e64db0bc2e 100644 --- a/docs/sources/userguide/usingdocker.md +++ b/docs/sources/userguide/usingdocker.md @@ -27,7 +27,7 @@ flags and arguments. # Usage: [sudo] docker [flags] [command] [arguments] .. # Example: - $ docker run -i -t ubuntu /bin/bash + $ sudo docker run -i -t ubuntu /bin/bash Let's see this in action by using the `docker version` command to return version information on the currently installed Docker client and daemon. @@ -114,8 +114,7 @@ We've specified an image: `training/webapp`. This image is a pre-built image we've created that contains a simple Python Flask web application. -Lastly, we've specified a command for our container to run: `python -app.py`. This launches our web application. +Lastly, we've specified a command for our container to run: `python app.py`. This launches our web application. > **Note:** > You can see more detail on the `docker run` command in the [command @@ -156,9 +155,9 @@ In this case Docker has exposed port 5000 (the default Python Flask port) on port 49155. Network port bindings are very configurable in Docker. In our last -example the `-P` flag is a shortcut for `-p 5000` that makes port 5000 -inside the container to a high port (from the range 49000 to 49900) on -the local Docker host. We can also bind Docker container's to specific +example the `-P` flag is a shortcut for `-p 5000` that maps port 5000 +inside the container to a high port (from the range 49153 to 65535) on +the local Docker host. We can also bind Docker containers to specific ports using the `-p` flag, for example: $ sudo docker run -d -p 5000:5000 training/webapp python app.py diff --git a/docs/sources/userguide/webapp1.png b/docs/sources/userguide/webapp1.png index 5653497f17..b92cc87d65 100644 Binary files a/docs/sources/userguide/webapp1.png and b/docs/sources/userguide/webapp1.png differ diff --git a/docs/theme/mkdocs/autoindex.html b/docs/theme/mkdocs/autoindex.html index cc4a41ec94..df1cc3eb09 100644 --- a/docs/theme/mkdocs/autoindex.html +++ b/docs/theme/mkdocs/autoindex.html @@ -1,3 +1,6 @@ +no_edit: true +auto_scroll_to_path: true + # Table of Contents {% for nav_item in nav %} diff --git a/docs/theme/mkdocs/base.html b/docs/theme/mkdocs/base.html index 2f518b5d59..4f669677b0 100644 --- a/docs/theme/mkdocs/base.html +++ b/docs/theme/mkdocs/base.html @@ -16,9 +16,10 @@ {% if canonical_url %}{% endif %} - + + {% if page_title != '**HIDDEN** - '+site_name %}{{ page_title }}{% else %}{{ site_name }}{% endif %} @@ -47,29 +48,8 @@
{% include "nav.html" %}
-{% if current_page.title != '**HIDDEN**' %} -
-
-

{{ current_page.title }}

-
- -
-{% endif %}
+{% if not meta.no_toc %}
+{% else %} +
+{% endif %} {% include "beta_warning.html" %} +{% if not meta.no_version_dropdown %} +
+ +
+{% endif %} {{ content }}
@@ -88,32 +88,19 @@
-{# -
-
-
-
- {% include "toc.html" %} -
-
- {% include "breadcrumbs.html" %} -
- {% include "version.html" %} - {{ content }} -
-
-
-
-
+{# need to find an unobtrusive way to add this back {% include "prev_next.html" %} #} {% include "footer.html" %} - + + + + @@ -134,16 +121,34 @@ piCId = '1482';