From 865649d7ac7e5ebb5b3debf65b626e96a88836b5 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Wed, 23 Oct 2013 16:03:04 -0600 Subject: [PATCH 01/71] Update sysvinit script to fix a few inconsistencies and be more useful/correct downstream --- contrib/init/sysvinit/docker | 49 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/contrib/init/sysvinit/docker b/contrib/init/sysvinit/docker index 5a457db78..6c0e182f0 100755 --- a/contrib/init/sysvinit/docker +++ b/contrib/init/sysvinit/docker @@ -6,43 +6,52 @@ # Required-Stop: $syslog $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: Linux container runtime -# Description: Linux container runtime +# Short-Description: Create lightweight, portable, self-sufficient containers. +# Description: +# Docker is an open-source project to easily create lightweight, portable, +# self-sufficient containers from any application. The same container that a +# developer builds and tests on a laptop can run at scale, in production, on +# VMs, bare metal, OpenStack clusters, public clouds and more. ### END INIT INFO -DOCKER=/usr/bin/docker -DOCKER_PIDFILE=/var/run/docker.pid +BASE=$(basename $0) + +DOCKER=/usr/bin/$BASE +DOCKER_PIDFILE=/var/run/$BASE.pid DOCKER_OPTS= PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin -# Check lxc-docker is present -[ -x $DOCKER ] || (log_failure_msg "docker not present"; exit 1) - # Get lsb functions . /lib/lsb/init-functions -if [ -f /etc/default/lxc ]; then - . /etc/default/lxc +if [ -f /etc/default/$BASE ]; then + . /etc/default/$BASE fi if [ "$1" = start ] && which initctl >/dev/null && initctl version | grep -q upstart; then exit 1 fi -check_root_id () -{ - if [ "$(id -u)" != "0" ]; then - log_failure_msg "Docker must be run as root"; exit 1 +# Check docker is present +if [ ! -x $DOCKER ]; then + log_failure_msg "$DOCKER not present or not executable" + exit 1 +fi + +fail_unless_root() { + if [ "$(id -u)" != '0' ]; then + log_failure_msg "Docker must be run as root" + exit 1 fi } case "$1" in start) - check_root_id || exit 1 - log_begin_msg "Starting Docker" + fail_unless_root + log_begin_msg "Starting Docker: $BASE" mount | grep cgroup >/dev/null || mount -t cgroup none /sys/fs/cgroup 2>/dev/null - start-stop-daemon --start --background $NO_CLOSE \ + start-stop-daemon --start --background \ --exec "$DOCKER" \ --pidfile "$DOCKER_PIDFILE" \ -- -d -p "$DOCKER_PIDFILE" \ @@ -51,15 +60,15 @@ case "$1" in ;; stop) - check_root_id || exit 1 - log_begin_msg "Stopping Docker" + fail_unless_root + log_begin_msg "Stopping Docker: $BASE" start-stop-daemon --stop \ --pidfile "$DOCKER_PIDFILE" log_end_msg $? ;; restart) - check_root_id || exit 1 + fail_unless_root docker_pid=`cat "$DOCKER_PIDFILE" 2>/dev/null` [ -n "$docker_pid" ] \ && ps -p $docker_pid > /dev/null 2>&1 \ @@ -68,7 +77,7 @@ case "$1" in ;; force-reload) - check_root_id || exit 1 + fail_unless_root $0 restart ;; From 91d721aaf485cd99b1fd491b6e153392882a4e72 Mon Sep 17 00:00:00 2001 From: rogaha Date: Mon, 28 Oct 2013 17:24:03 -0700 Subject: [PATCH 02/71] - Added volumes documentation --- docs/sources/use/working_with_volumes.rst | 87 +++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docs/sources/use/working_with_volumes.rst diff --git a/docs/sources/use/working_with_volumes.rst b/docs/sources/use/working_with_volumes.rst new file mode 100644 index 000000000..a0e1fbf9e --- /dev/null +++ b/docs/sources/use/working_with_volumes.rst @@ -0,0 +1,87 @@ +:title: Working with Volumes +:description: How to create and share volumes +:keywords: Examples, Usage, volume, docker, documentation, examples + +.. _volume_def: + +Data Volume +=========== + +.. versionadded:: v0.3.0 + Data volumes have been available since version 1 of the + :doc:`../api/docker_remote_api` + +A *data volume* is a specially-designated directory within one or more +containers that bypasses the :ref:`ufs_def` to provide several useful +features for persistant or shared data: + +* **Data volumes can be shared and reused between containers.** This + is the feature that makes data volumes so powerful. You can use it + for anything from hot database upgrades to custom backup or + replication tools. See the example below. +* **Changes to a data volume are made directly**, without the overhead + of a copy-on-write mechanism. This is good for very large files. +* **Changes to a data volume will not be included at the next commit** + because they are not recorded as regular filesystem changes in the + top layer of the :ref:`ufs_def` + +Each container can have zero or more data volumes. + +Getting Started +............... + + + +Using data volumes is as simple as adding a new flag: ``-v``. The parameter ``-v`` can be used more than once in order to create more volumes within the new container. The example below shows the instruction to create a container with two new volumes:: + + docker run -v /var/volume1 -v /var/volume2 shykes/couchdb + +For a Dockerfile, the VOLUME instruction will add one or more new volumes to any container created from the image:: + + VOLUME ["/var/volume1", "/var/volume2"] + + +Create a new container using existing volumes from an existing container: +--------------------------------------------------------------------------- + + +The command below creates a new container which is runnning as daemon ``-d`` and with one volume ``/var/lib/couchdb``:: + + COUCH1=$(sudo docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03) + +From the container id of that previous container ``$COUCH1`` it's possible to create new container sharing the same volume using the parameter ``-volumes-from container_id``:: + + COUCH2=$(sudo docker run -d -volumes-from $COUCH1 shykes/couchdb:2013-05-03) + +Now, the second container has the all the information from the first volume. + + +Create a new container which mounts a host directory into it: +------------------------------------------------------------- + + -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. + If "host-dir" is missing, then docker creates a new volume. + + This is not available for a Dockerfile due the portability and sharing purpose of it. The [host-dir] volumes is something 100% host dependent and will break on any other machine. + +For example:: + + sudo docker run -v /var/logs:/var/host_logs:ro shykes/couchdb:2013-05-03 + +The command above mounts the host directory ``/var/logs`` into the container with read only permissions as ``/var/host_logs``. + +.. versionadded:: v0.5.0 + +Volumes & persistent data storage +================================= + +Current status +.............. + +Docker has volumes which can be reused from container to container. These volumes have the following limitations: + +* they can't be imported and exported (without piping data into and out of the container) +* they can't be backed up and restored from backups (without piping data into and out of the container) +* they can't be stored on custom storage (e.g.: use high IOPS storage for a volume and regular IOPS storage for others) +* it's not possible to cherry pick the volumes to be used from an old container in a new container +* it's not possible to manage the volumes after deleting the containers to which they were attached \ No newline at end of file From dd9f4524d1a829a394f5d96da0c1e6338c476019 Mon Sep 17 00:00:00 2001 From: Dan Hirsch Date: Sun, 27 Oct 2013 21:17:10 -0400 Subject: [PATCH 03/71] Make /events API send headers immediately Go's net/http API does not send headers until the first call to ResponseWriter.Write(). Ordinarily, this is fine, because in most cases, responses are returned immediately. However, for the events API, nothing is written until some event is sent, which causes timeouts and/or hangs in some HTTP client APIs, which wait for headers before returning from the "make request" call. --- api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/api.go b/api.go index 6c1c47a00..517fe5992 100644 --- a/api.go +++ b/api.go @@ -236,6 +236,7 @@ func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } w.Header().Set("Content-Type", "application/json") wf := utils.NewWriteFlusher(w) + wf.Write([]byte{}) if since != 0 { // If since, send previous events that happened after the timestamp for _, event := range srv.events { From fb4672c52c050ce08c7432d2d149e37b0056e8f6 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 29 Oct 2013 19:03:41 -0700 Subject: [PATCH 04/71] do not split last column docker top --- commands.go | 2 +- docs/sources/commandline/cli.rst | 2 +- server.go | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/commands.go b/commands.go index 4a7c4b8d2..a8eae4476 100644 --- a/commands.go +++ b/commands.go @@ -682,7 +682,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error { } func (cli *DockerCli) CmdTop(args ...string) error { - cmd := Subcmd("top", "CONTAINER", "Lookup the running processes of a container") + cmd := Subcmd("top", "CONTAINER [ps OPTIONS]", "Lookup the running processes of a container") if err := cmd.Parse(args); err != nil { return nil } diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 6efa9b1eb..cb4d6698e 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -728,7 +728,7 @@ to the newly created container. :: - Usage: docker top CONTAINER + Usage: docker top CONTAINER [ps OPTIONS] Lookup the running processes of a container diff --git a/server.go b/server.go index 2cb677117..b3845bd79 100644 --- a/server.go +++ b/server.go @@ -350,7 +350,11 @@ func (srv *Server) ContainerTop(name, ps_args string) (*APITop, error) { } // no scanner.Text because we skip container id for scanner.Scan() { - words = append(words, scanner.Text()) + if i != 0 && len(words) == len(procs.Titles) { + words[len(words)-1] = fmt.Sprintf("%s %s", words[len(words)-1], scanner.Text()) + } else { + words = append(words, scanner.Text()) + } } if i == 0 { procs.Titles = words From 652f07bb59e9c46d5f3e375c4a99d0b23771e0ad Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 31 Oct 2013 11:10:25 -0700 Subject: [PATCH 05/71] Add -P flag to publish all exposed ports --- container.go | 6 ++++++ docs/sources/commandline/cli.rst | 1 + 2 files changed, 7 insertions(+) diff --git a/container.go b/container.go index 944893267..c89dc8db4 100644 --- a/container.go +++ b/container.go @@ -97,6 +97,7 @@ type HostConfig struct { LxcConf []KeyValuePair PortBindings map[Port][]PortBinding Links []string + PublishAllPorts bool } type BindMap struct { @@ -168,6 +169,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)") cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") cmd.String("name", "", "Assign a name to the container") + flPublishAll := cmd.Bool("P", false, "Publish all exposed ports to the host interfaces") if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit { //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") @@ -327,6 +329,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, LxcConf: lxcConf, PortBindings: portBindings, Links: flLinks, + PublishAllPorts: *flPublishAll, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { @@ -1111,6 +1114,9 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error { for port := range portSpecs { binding := bindings[port] + if hostConfig.PublishAllPorts && len(binding) == 0 { + binding = append(binding, PortBinding{}) + } for i := 0; i < len(binding); i++ { b := binding[i] nat, err := iface.AllocatePort(port, b) diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 52a83c58f..537ee4682 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -584,6 +584,7 @@ network communication. -expose=[]: Expose a port from the container without publishing it to your host -link="": Add link to another container (name:alias) -name="": Assign the specified name to the container. If no name is specific docker will generate a random name + -P=false: Publish all exposed ports to the host interfaces Examples -------- From 40ed10cc32fe6dbf18d13a43d42a1a9bed0df902 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 31 Oct 2013 18:39:44 -0700 Subject: [PATCH 06/71] fix inspect when it returns nothing valid --- commands.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/commands.go b/commands.go index 0e06f4794..05c8b6bb0 100644 --- a/commands.go +++ b/commands.go @@ -667,9 +667,11 @@ func (cli *DockerCli) CmdInspect(args ...string) error { } indented.WriteString(",") } - // Remove trailling ',' - indented.Truncate(indented.Len() - 1) + if indented.Len() > 0 { + // Remove trailling ',' + indented.Truncate(indented.Len() - 1) + } fmt.Fprintf(cli.out, "[") if _, err := io.Copy(cli.out, indented); err != nil { return err From 7848007c3ab41b3e1d733f9235a69ec59e04ead1 Mon Sep 17 00:00:00 2001 From: Yang Bai Date: Fri, 1 Nov 2013 09:42:44 +0800 Subject: [PATCH 07/71] rewrite protocol check with switch-case in Server.Daemon --- server.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server.go b/server.go index 815ed3fe7..53e8da131 100644 --- a/server.go +++ b/server.go @@ -6,10 +6,10 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/auth" + "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/gograph" "github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/utils" - "github.com/dotcloud/docker/engine" "io" "io/ioutil" "log" @@ -17,14 +17,14 @@ import ( "net/url" "os" "os/exec" + "os/signal" "path" "path/filepath" "runtime" "strings" "sync" - "time" "syscall" - "os/signal" + "time" ) func (srv *Server) Close() error { @@ -70,13 +70,16 @@ func (srv *Server) Daemon() error { chErrors := make(chan error, len(protoAddrs)) for _, protoAddr := range protoAddrs { protoAddrParts := strings.SplitN(protoAddr, "://", 2) - if protoAddrParts[0] == "unix" { - syscall.Unlink(protoAddrParts[1]) - } else if protoAddrParts[0] == "tcp" { + switch protoAddrParts[0] { + case "unix": + if err := syscall.Unlink(protoAddrParts[1]); err != nil && !os.IsNotExist(err) { + log.Fatal(err) + } + case "tcp": if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") { log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } - } else { + default: return fmt.Errorf("Invalid protocol format.") } go func() { @@ -92,7 +95,6 @@ func (srv *Server) Daemon() error { return nil } - func (srv *Server) DockerVersion() APIVersion { return APIVersion{ Version: VERSION, From a89a98e59432109d99c08b43f670df7429145172 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 31 Oct 2013 18:44:16 -0700 Subject: [PATCH 08/71] fix inspect error message --- commands.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/commands.go b/commands.go index 05c8b6bb0..20f5855e1 100644 --- a/commands.go +++ b/commands.go @@ -654,7 +654,11 @@ func (cli *DockerCli) CmdInspect(args ...string) error { if err != nil { obj, _, err = cli.call("GET", "/images/"+name+"/json", nil) if err != nil { - fmt.Fprintf(cli.err, "No such image or container: %s\n", name) + if strings.Contains(err.Error(), "No such") { + fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name) + } else { + fmt.Fprintf(cli.err, "%s", err) + } status = 1 continue } @@ -669,7 +673,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error { } if indented.Len() > 0 { - // Remove trailling ',' + // Remove trailing ',' indented.Truncate(indented.Len() - 1) } fmt.Fprintf(cli.out, "[") From 7f07ae9e42061f06886441994977a238013dd5ff Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 31 Oct 2013 18:33:42 -0700 Subject: [PATCH 09/71] check port is valid --- utils.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils.go b/utils.go index c71c95ba3..81715881a 100644 --- a/utils.go +++ b/utils.go @@ -225,6 +225,12 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, if containerPort == "" { return nil, nil, fmt.Errorf("No port specified: %s", rawPort) } + if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil { + return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort) + } + if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil { + return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort) + } port := NewPort(proto, containerPort) if _, exists := exposedPorts[port]; !exists { From 02b52024328996e2028d7d953ef6a9c1e2e50744 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 24 Oct 2013 12:20:34 -0700 Subject: [PATCH 10/71] update docker search to reflect future changes of the api --- api_params.go | 5 ----- commands.go | 19 ++++++++++++++----- registry/registry.go | 14 +++++++++++--- server.go | 12 ++---------- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/api_params.go b/api_params.go index 3c53d0f2a..f666ac239 100644 --- a/api_params.go +++ b/api_params.go @@ -78,11 +78,6 @@ type APIContainersOld struct { SizeRootFs int64 } -type APISearch struct { - Name string - Description string -} - type APIID struct { ID string `json:"Id"` } diff --git a/commands.go b/commands.go index 0e06f4794..ee87781e9 100644 --- a/commands.go +++ b/commands.go @@ -1420,19 +1420,19 @@ func (cli *DockerCli) CmdSearch(args ...string) error { return err } - outs := []APISearch{} + outs := []registry.SearchResult{} err = json.Unmarshal(body, &outs) if err != nil { return err } fmt.Fprintf(cli.out, "Found %d results matching your query (\"%s\")\n", len(outs), cmd.Arg(0)) - w := tabwriter.NewWriter(cli.out, 33, 1, 3, ' ', 0) - fmt.Fprintf(w, "NAME\tDESCRIPTION\n") + w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0) + fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n") _, width := cli.getTtySize() if width == 0 { width = 45 } else { - width = width - 33 //remote the first column + width = width - 10 - 54 //remote the first column } for _, out := range outs { desc := strings.Replace(out.Description, "\n", " ", -1) @@ -1440,7 +1440,16 @@ func (cli *DockerCli) CmdSearch(args ...string) error { if !*noTrunc && len(desc) > width { desc = utils.Trunc(desc, width-3) + "..." } - fmt.Fprintf(w, "%s\t%s\n", out.Name, desc) + fmt.Fprintf(w, "%s\t%s\t%d\t", out.Name, desc, out.StarCount) + if out.IsOfficial { + fmt.Fprint(w, "[OK]") + + } + fmt.Fprint(w, "\t") + if out.IsTrusted { + fmt.Fprint(w, "[OK]") + } + fmt.Fprint(w, "\n") } w.Flush() return nil diff --git a/registry/registry.go b/registry/registry.go index 9e49f3566..f02e3cf47 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -615,10 +615,18 @@ func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig { } } +type SearchResult struct { + StarCount int `json:"star_count"` + IsOfficial bool `json:"is_official"` + Name string `json:"name"` + IsTrusted bool `json:"is_trusted"` + Description string `json:"description"` +} + type SearchResults struct { - Query string `json:"query"` - NumResults int `json:"num_results"` - Results []map[string]string `json:"results"` + Query string `json:"query"` + NumResults int `json:"num_results"` + Results []SearchResult `json:"results"` } type RepositoryData struct { diff --git a/server.go b/server.go index 815ed3fe7..40e38a661 100644 --- a/server.go +++ b/server.go @@ -183,7 +183,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { return fmt.Errorf("No such container: %s", name) } -func (srv *Server) ImagesSearch(term string) ([]APISearch, error) { +func (srv *Server) ImagesSearch(term string) ([]registry.SearchResult, error) { r, err := registry.NewRegistry(srv.runtime.config.Root, nil, srv.HTTPRequestFactory(nil)) if err != nil { return nil, err @@ -192,15 +192,7 @@ func (srv *Server) ImagesSearch(term string) ([]APISearch, error) { if err != nil { return nil, err } - - var outs []APISearch - for _, repo := range results.Results { - var out APISearch - out.Description = repo["description"] - out.Name = repo["name"] - outs = append(outs, out) - } - return outs, nil + return results.Results, nil } func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) { From 747967b4a4e2bfa429a76d20db77b990d23b1a0f Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 31 Oct 2013 19:21:35 -0700 Subject: [PATCH 11/71] rebase and add some filters --- commands.go | 8 ++++++-- contrib/completion/bash/docker | 2 +- docs/sources/commandline/cli.rst | 7 +++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/commands.go b/commands.go index ee87781e9..51733035c 100644 --- a/commands.go +++ b/commands.go @@ -1403,8 +1403,10 @@ func (cli *DockerCli) CmdAttach(args ...string) error { } func (cli *DockerCli) CmdSearch(args ...string) error { - cmd := Subcmd("search", "NAME", "Search the docker index for images") + cmd := Subcmd("search", "TERM", "Search the docker index for images") noTrunc := cmd.Bool("notrunc", false, "Don't truncate output") + trusted := cmd.Bool("trusted", false, "Only show trusted builds") + stars := cmd.Int("stars", 0, "Only displays with at least xxx stars") if err := cmd.Parse(args); err != nil { return nil } @@ -1425,7 +1427,6 @@ func (cli *DockerCli) CmdSearch(args ...string) error { if err != nil { return err } - fmt.Fprintf(cli.out, "Found %d results matching your query (\"%s\")\n", len(outs), cmd.Arg(0)) w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0) fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n") _, width := cli.getTtySize() @@ -1435,6 +1436,9 @@ func (cli *DockerCli) CmdSearch(args ...string) error { width = width - 10 - 54 //remote the first column } for _, out := range outs { + if (*trusted && !out.IsTrusted) || (*stars > out.StarCount) { + continue + } desc := strings.Replace(out.Description, "\n", " ", -1) desc = strings.Replace(desc, "\r", " ", -1) if !*noTrunc && len(desc) > width { diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 5a91ab0f1..8e535285e 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -426,7 +426,7 @@ _docker_run() _docker_search() { - COMPREPLY=( $( compgen -W "-notrunc" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-notrunc" "-stars" "-trusted" -- "$cur" ) ) } _docker_start() diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index c6579da35..3256310b0 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -683,8 +683,11 @@ to the newly created container. Usage: docker search TERM - Searches for the TERM parameter on the Docker index and prints out - a list of repositories that match. + Search the docker index for images + + -notrunc=false: Don't truncate output + -stars=0: Only displays with at least xxx stars + -trusted=false: Only show trusted builds .. _cli_start: From db2e2e831ba45f173591766160699ec0b0c77f0d Mon Sep 17 00:00:00 2001 From: Andrew Duckworth Date: Fri, 1 Nov 2013 13:59:25 +0000 Subject: [PATCH 12/71] Update windows.rst Added further information regarding how to access BIOS menus on Windows machines (laptops). My laptop required restarting to find the options and this information will undoubtedly help other users. --- docs/sources/installation/windows.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/sources/installation/windows.rst b/docs/sources/installation/windows.rst index a6b30aa41..b487606a4 100644 --- a/docs/sources/installation/windows.rst +++ b/docs/sources/installation/windows.rst @@ -208,6 +208,10 @@ configuration / Device configuration) .. image:: images/win/hp_bios_vm.JPG +On some machines the BIOS menu can only be accessed before startup. +To access BIOS in this scenario you should restart your computer and +press ESC/Enter when prompted to access the boot and BIOS controls. Typically +the option to allow virtualization is contained within the BIOS/Security menu. Docker is not installed ``````````````````````` From f2ebccb5aab6600094d92bdb6d4a8ab62bb519d2 Mon Sep 17 00:00:00 2001 From: Nicolas Dudebout Date: Fri, 1 Nov 2013 12:14:43 -0400 Subject: [PATCH 13/71] Corrects the TCP section of the basics use --- docs/sources/use/basics.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/sources/use/basics.rst b/docs/sources/use/basics.rst index a9eda88a6..0097b6836 100644 --- a/docs/sources/use/basics.rst +++ b/docs/sources/use/basics.rst @@ -138,22 +138,19 @@ Listing all running containers sudo docker ps -Expose a service on a TCP port +Bind a service on a TCP port ------------------------------ .. code-block:: bash - # Expose port 4444 of this container, and tell netcat to listen on it + # Bind port 4444 of this container, and tell netcat to listen on it JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l 4444) # Which public port is NATed to my container? PORT=$(sudo docker port $JOB 4444 | awk -F: '{ print $2 }') - # Connect to the public port via the host's public address - # Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work. - # Replace *eth0* according to your local interface name. - IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }') - echo hello world | nc $IP $PORT + # Connect to the public port + echo hello world | nc 127.0.0.1 $PORT # Verify that the network connection worked echo "Daemon received: $(sudo docker logs $JOB)" @@ -183,4 +180,3 @@ You now have a image state from which you can create new instances. Read more about :ref:`working_with_the_repository` or continue to the complete :ref:`cli` - From c0e51a8f27defe3d4dde0b5805d3801cf25a6ce2 Mon Sep 17 00:00:00 2001 From: Nicolas Dudebout Date: Thu, 31 Oct 2013 00:10:29 -0400 Subject: [PATCH 14/71] Rework the port direction documentation + remove PUBLIC and PRIVATE keywords + add the + add the -expose flag through a simple link example + update the meaning of the EXPOSE command --- docs/sources/commandline/cli.rst | 64 +++++------- docs/sources/use/builder.rst | 10 +- docs/sources/use/port_redirection.rst | 141 ++++++++++++++++++++++---- 3 files changed, 156 insertions(+), 59 deletions(-) diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index c6579da35..f3f9e7752 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -41,7 +41,7 @@ To stop a container, use ``docker stop`` To kill the container, use ``docker kill`` .. _cli_attach_examples: - + Examples: ~~~~~~~~~ @@ -55,8 +55,8 @@ Examples: Mem: 373572k total, 355560k used, 18012k free, 27872k buffers Swap: 786428k total, 0k used, 786428k free, 221740k cached - PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 1 root 20 0 17200 1116 912 R 0 0.3 0:00.03 top + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 1 root 20 0 17200 1116 912 R 0 0.3 0:00.03 top top - 02:05:55 up 3:05, 0 users, load average: 0.01, 0.02, 0.05 Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie @@ -64,8 +64,8 @@ Examples: Mem: 373572k total, 355244k used, 18328k free, 27872k buffers Swap: 786428k total, 0k used, 786428k free, 221776k cached - PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top top - 02:05:58 up 3:06, 0 users, load average: 0.01, 0.02, 0.05 @@ -74,9 +74,9 @@ Examples: Mem: 373572k total, 355780k used, 17792k free, 27880k buffers Swap: 786428k total, 0k used, 786428k free, 221776k cached - PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top - ^C$ + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 1 root 20 0 17208 1144 932 R 0 0.3 0:00.03 top + ^C$ $ sudo docker stop $ID .. _cli_build: @@ -157,7 +157,7 @@ by using the ``git://`` schema. -m="": Commit message -author="": Author (eg. "John Hannibal Smith " - -run="": Configuration to be applied when the image is launched with `docker run`. + -run="": Configuration to be applied when the image is launched with `docker run`. (ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}') Full -run example (multiline is ok within a single quote ``'``) @@ -239,7 +239,7 @@ Shell 1: Listening for events ............................. .. code-block:: bash - + $ sudo docker events Shell 2: Start and Stop a Container @@ -405,7 +405,7 @@ Insert file from github Usage: docker kill CONTAINER [CONTAINER...] Kill a running container (Send SIGKILL) - + The main process inside the container will be sent SIGKILL. .. _cli_login: @@ -515,7 +515,7 @@ The main process inside the container will be sent SIGKILL. Remove one or more containers -link="": Remove the link instead of the actual container - + Examples: ~~~~~~~~~ @@ -620,58 +620,51 @@ use-cases, like running Docker within Docker. docker run -w /path/to/dir/ -i -t ubuntu pwd -The ``-w`` lets the command being executed inside directory given, -here /path/to/dir/. If the path does not exists it is created inside the +The ``-w`` lets the command being executed inside directory given, +here /path/to/dir/. If the path does not exists it is created inside the container. .. code-block:: bash docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd -The ``-v`` flag mounts the current working directory into the container. -The ``-w`` lets the command being executed inside the current +The ``-v`` flag mounts the current working directory into the container. +The ``-w`` lets the command being executed inside the current working directory, by changing into the directory to the value returned by ``pwd``. So this combination executes the command using the container, but inside the current working directory. .. code-block:: bash - docker run -p 127.0.0.0::80 ubuntu bash + docker run -p 127.0.0.0:80:8080 ubuntu bash -The ``-p`` flag now allows you to bind a port to a specific -interface of the host machine. In this example port ``80`` of the -container will have a dynamically allocated port bound to 127.0.0.1 -of the host. - -.. code-block:: bash - - docker run -p 127.0.0.1:80:80 ubuntu bash - -This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your -host machine. +This binds port ``8080`` of the container to port ``80`` on 127.0.0.1 of the +host machine. :ref:`port_redirection` explains in detail how to manipulate ports +in Docker. .. code-block:: bash docker run -expose 80 ubuntu bash -This will expose port ``80`` of the container for use within a link -without publishing the port to the host system's interfaces. +This exposes port ``80`` of the container for use within a link without +publishing the port to the host system's interfaces. :ref:`port_redirection` +explains in detail how to manipulate ports in Docker. .. code-block:: bash docker run -name console -t -i ubuntu bash -This will create and run a new container with the container name +This will create and run a new container with the container name being ``console``. .. code-block:: bash docker run -link /redis:redis -name console ubuntu bash -The ``-link`` flag will link the container named ``/redis`` into the +The ``-link`` flag will link the container named ``/redis`` into the newly created container with the alias ``redis``. The new container can access the network and environment of the redis container via -environment variables. The ``-name`` flag will assign the name ``console`` +environment variables. The ``-name`` flag will assign the name ``console`` to the newly created container. .. _cli_search: @@ -712,7 +705,7 @@ to the newly created container. Stop a running container (Send SIGTERM, and then SIGKILL after grace period) -t=10: Number of seconds to wait for the container to stop before killing it. - + The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL .. _cli_tag: @@ -757,6 +750,3 @@ Show the version of the docker client, daemon, and latest released version. Usage: docker wait [OPTIONS] NAME Block until a container stops, then print its exit code. - - - diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index 4341826f4..d1747c3fb 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -174,10 +174,10 @@ override the default specified in CMD. ``EXPOSE [...]`` -The ``EXPOSE`` instruction sets ports to be publicly exposed when -running the image. This is functionally equivalent to running ``docker -commit -run '{"PortSpecs": ["", ""]}'`` outside the -builder. Take a look at :ref:`port_redirection` for more information. +The ``EXPOSE`` instruction exposes ports for use within links. This is +functionally equivalent to running ``docker commit -run '{"PortSpecs": +["", ""]}'`` outside the builder. Refer to +:ref:`port_redirection` for detailed information. 3.6 ENV ------- @@ -243,7 +243,7 @@ The copy obeys the following rules: considered a regular file and the contents of ```` will be written at ````. * If ```` doesn't exist, it is created along with all missing - directories in its path. + directories in its path. .. _entrypoint_def: diff --git a/docs/sources/use/port_redirection.rst b/docs/sources/use/port_redirection.rst index 5c321d429..adce1cba6 100644 --- a/docs/sources/use/port_redirection.rst +++ b/docs/sources/use/port_redirection.rst @@ -8,29 +8,136 @@ Port redirection ================ -Docker can redirect public TCP and UDP ports to your container, so it can be -reached over the network. Port redirection is done on ``docker run`` -using the -p flag. - -A port redirect is specified as *PUBLIC:PRIVATE*, where TCP port -*PUBLIC* will be redirected to TCP port *PRIVATE*. As a special case, -the public port can be omitted, in which case a random public port -will be allocated. +Interacting with a service is commonly done through a connection to a +port. When this service runs inside a container, one can connect to +the port after finding the IP address of the container as follows: .. code-block:: bash - # A random PUBLIC port is redirected to PRIVATE port 80 on the container - sudo docker run -p 80 + # Find IP address of container with ID + docker inspect | grep IPAddress | cut -d '"' -f 4 - # PUBLIC port 80 is redirected to PRIVATE port 80 - sudo docker run -p 80:80 +However, this IP address is local to the host system and the container +port is not reachable by the outside world. Furthermore, even if the +port is used locally, e.g. by another container, this method is +tedious as the IP address of the container changes every time it +starts. -To redirect a UDP port the redirection must be expressed as *PUBLIC:PRIVATE/udp*: +Docker addresses these two problems and give a simple and robust way +to access services running inside containers. + +To allow non-local clients to reach the service running inside the +container, Docker provide ways to bind the container port to an +interface of the host system. To simplify communication between +containers, Docker provides the linking mechanism. + +Binding a port to an host interface +----------------------------------- + +To bind a port of the container to a specific interface of the host +system, use the ``-p`` parameter of the ``docker run`` command: .. code-block:: bash - # PUBLIC port 5300 is redirected to the PRIVATE port 53 using UDP - sudo docker run -p 5300:53/udp + # General syntax + docker run -p [([:[host_port]])|():][/udp] -Default port redirects can be built into a container with the -``EXPOSE`` build command. +When no host interface is provided, the port is bound to all available +interfaces of the host machine (aka INADDR_ANY, or 0.0.0.0).When no host port is +provided, one is dynamically allocated. The possible combinations of options for +TCP port are the following: + +.. code-block:: bash + + # Bind TCP port 8080 of the container to TCP port 80 on 127.0.0.1 of the host machine. + docker run -p 127.0.0.1:80:8080 + + # Bind TCP port 8080 of the container to a dynamically allocated TCP port on 127.0.0.1 of the host machine. + docker run -p 127.0.0.1::8080 + + # Bind TCP port 8080 of the container to TCP port 80 on all available interfaces of the host machine. + docker run -p 80:8080 + + # Bind TCP port 8080 of the container to a dynamically allocated TCP port on all available interfaces of the host machine. + docker run -p 8080 + +UDP ports can also be bound by adding a trailing ``/udp``. All the +combinations described for TCP work. Here is only one example: + +.. code-block:: bash + + # Bind UDP port 5353 of the container to UDP port 53 on 127.0.0.1 of the host machine. + docker run -p 127.0.0.1:53:5353/udp + +The command ``docker port`` lists the interface and port on the host +machine bound to a given container port. It is useful when using +dynamically allocated ports: + +.. code-block:: bash + + # Bind to a dynamically allocated port + docker run -p 127.0.0.1::8080 -name dyn-bound + + # Lookup the actual port + docker port dyn-bound 8080 + 127.0.0.1:49160 + + +Linking a container +------------------- + +Communication between two containers can also be established in a +docker-specific way called linking. + +To briefly present the concept of linking, let us consider two +containers: ``server``, containing the service, and ``client``, +accessing the service. Once ``server`` is running, ``client`` is +started and links to server. Linking sets environment variables in +``client`` giving it some information about ``server``. In this sense, +linking is a method of service discovery. + +Let us now get back to our topic of interest; communication between +the two containers. We mentioned that the tricky part about this +communication was that the IP address of ``server`` was not +fixed. Therefore, some of the environment variables are going to be +used to inform ``client`` about this IP address. This process called +exposure, is possible because ``client`` is started after ``server`` +has been started. + +Here is a full example. On ``server``, the port of interest is +exposed. The exposure is done either through the ``-expose`` parameter +to the ``docker run`` command, or the ``EXPOSE`` build command in a +Dockerfile: + +.. code-block:: bash + + # Expose port 80 + docker run -expose 80 -name server + +The ``client`` then links to the ``server``: + +.. code-block:: bash + + # Link + docker run -name client -link server:linked-server + +``client`` locally refers to ``server`` as ``linked-server``. The +following environment variables, among others, are available on +``client``: + +.. code-block:: bash + + # The default protocol, ip, and port of the service running in the container + LINKED-SERVER_PORT=tcp://172.17.0.8:80 + + # A specific protocol, ip, and port of various services + LINKED-SERVER_PORT_80_TCP=tcp://172.17.0.8:80 + LINKED-SERVER_PORT_80_TCP_PROTO=tcp + LINKED-SERVER_PORT_80_TCP_ADDR=172.17.0.8 + LINKED-SERVER_PORT_80_TCP_PORT=80 + +This tells ``client`` that a service is running on port 80 of +``server`` and that ``server`` is accessible at the IP address +172.17.0.8 + +Note: Using the ``-p`` parameter also exposes the port.. From a963ff5d8df4e6d0e42dd669d3d60aafb8f880a7 Mon Sep 17 00:00:00 2001 From: Dan Hirsch Date: Fri, 1 Nov 2013 13:11:21 -0400 Subject: [PATCH 15/71] Added explicit Flush method to utils.WriteFlusher --- api.go | 10 +++++----- utils/utils.go | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/api.go b/api.go index 517fe5992..44e83349c 100644 --- a/api.go +++ b/api.go @@ -72,12 +72,12 @@ func httpError(w http.ResponseWriter, err error) { statusCode = http.StatusUnauthorized } else if strings.Contains(err.Error(), "hasn't been activated") { statusCode = http.StatusForbidden - } - + } + if err != nil { utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error()) - http.Error(w, err.Error(), statusCode) - } + http.Error(w, err.Error(), statusCode) + } } func writeJSON(w http.ResponseWriter, code int, v interface{}) error { @@ -236,7 +236,7 @@ func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } w.Header().Set("Content-Type", "application/json") wf := utils.NewWriteFlusher(w) - wf.Write([]byte{}) + wf.Flush() if since != 0 { // If since, send previous events that happened after the timestamp for _, event := range srv.events { diff --git a/utils/utils.go b/utils/utils.go index 389203b98..a90f4e9f0 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -616,6 +616,13 @@ func (wf *WriteFlusher) Write(b []byte) (n int, err error) { return n, err } +// Flush the stream immediately. +func (wf *WriteFlusher) Flush() { + wf.Lock() + defer wf.Unlock() + wf.flusher.Flush() +} + func NewWriteFlusher(w io.Writer) *WriteFlusher { var flusher http.Flusher if f, ok := w.(http.Flusher); ok { From 9ebac49be00ad140f7a8ef156b9ae7850dd8c273 Mon Sep 17 00:00:00 2001 From: Gurjeet Singh Date: Fri, 1 Nov 2013 12:31:50 -0500 Subject: [PATCH 16/71] Add myself as an author 7e09c633 One of my patches was incorporated into Docker, and at that time I didn't know that I was supposed to add myself into this list of contributors. Commit ID: 7e09c6332de11538e170b4d25ccd5bfa068ffd1e And a few other patches are already in the pipeline. --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 579ee0c48..152e64b1d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -69,6 +69,7 @@ Gabriel Monroy Gareth Rushgrove Greg Thornton Guillaume J. Charmes +Gurjeet Singh Guruprasad Harley Laue Hector Castro From 31638ab2ad2a5380d447780f05f7aa078c9421f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Petazzoni?= Date: Thu, 31 Oct 2013 14:58:43 -0700 Subject: [PATCH 17/71] Refactor HostConfig and escape apparmor confinement --- Dockerfile | 12 +-- api_test.go | 21 ++-- buildfile.go | 3 +- container.go | 99 +++++++++---------- container_test.go | 76 +++++++------- docs/sources/contributing/devenvironment.rst | 11 +-- hack/dind | 8 ++ hack/infrastructure/docker-ci/deployment.py | 5 - .../docker-ci/nightlyrelease/Dockerfile | 5 +- .../docker-ci/nightlyrelease/dockerbuild | 5 +- lxc_template.go | 35 ++++--- runtime.go | 42 +++++++- runtime_test.go | 31 +++--- server.go | 9 +- server_test.go | 6 +- utils_test.go | 13 +-- 16 files changed, 197 insertions(+), 184 deletions(-) diff --git a/Dockerfile b/Dockerfile index ff80e4ad9..eefee0344 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,24 +4,24 @@ # # # Assemble the full dev environment. This is slow the first time. # docker build -t docker . -# # Apparmor messes with privileged mode: disable it -# /etc/init.d/apparmor stop ; /etc/init.d/apparmor teardown # # # Mount your source in an interactive container for quick testing: -# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -lxc-conf=lxc.aa_profile=unconfined -i -t docker bash -# +# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -i -t docker bash # # # Run the test suite: -# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test +# docker run -privileged docker hack/make.sh test # # # Publish a release: -# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined \ +# 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:12.04 diff --git a/api_test.go b/api_test.go index 34c89f829..fcca8ce83 100644 --- a/api_test.go +++ b/api_test.go @@ -499,8 +499,7 @@ func TestGetContainersTop(t *testing.T) { container.WaitTimeout(2 * time.Second) }() - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -704,8 +703,7 @@ func TestPostContainersKill(t *testing.T) { } defer runtime.Destroy(container) - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -747,8 +745,7 @@ func TestPostContainersRestart(t *testing.T) { } defer runtime.Destroy(container) - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -855,8 +852,7 @@ func TestPostContainersStop(t *testing.T) { } defer runtime.Destroy(container) - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -903,8 +899,7 @@ func TestPostContainersWait(t *testing.T) { } defer runtime.Destroy(container) - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -947,8 +942,7 @@ func TestPostContainersAttach(t *testing.T) { defer runtime.Destroy(container) // Start the process - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -1037,8 +1031,7 @@ func TestPostContainersAttachStderr(t *testing.T) { defer runtime.Destroy(container) // Start the process - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } diff --git a/buildfile.go b/buildfile.go index 41828f33d..a6322a82a 100644 --- a/buildfile.go +++ b/buildfile.go @@ -388,8 +388,7 @@ func (b *buildFile) run() (string, error) { } //start the container - hostConfig := &HostConfig{} - if err := c.Start(hostConfig); err != nil { + if err := c.Start(); err != nil { return "", err } diff --git a/container.go b/container.go index 52d4110ea..ea34f6c68 100644 --- a/container.go +++ b/container.go @@ -60,11 +60,15 @@ type Container struct { 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 + VolumesRW map[string]bool + hostConfig *HostConfig activeLinks map[string]*Link } +// Note: the Config structure should hold only portable information about the container. +// Here, "portable" means "independent from the host we are running on". +// Non-portable information *should* appear in HostConfig. type Config struct { Hostname string Domainname string @@ -89,13 +93,13 @@ type Config struct { WorkingDir string Entrypoint []string NetworkDisabled bool - Privileged bool } type HostConfig struct { Binds []string ContainerIDFile string LxcConf []KeyValuePair + Privileged bool PortBindings map[Port][]PortBinding Links []string PublishAllPorts bool @@ -320,7 +324,6 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Volumes: flVolumes, VolumesFrom: strings.Join(flVolumesFrom, ","), Entrypoint: entrypoint, - Privileged: *flPrivileged, WorkingDir: *flWorkingDir, } @@ -328,6 +331,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Binds: binds, ContainerIDFile: *flContainerIDFile, LxcConf: lxcConf, + Privileged: *flPrivileged, PortBindings: portBindings, Links: flLinks, PublishAllPorts: *flPublishAll, @@ -416,7 +420,7 @@ func (container *Container) FromDisk() error { if err := json.Unmarshal(data, container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") { return err } - return nil + return container.readHostConfig() } func (container *Container) ToDisk() (err error) { @@ -424,23 +428,31 @@ func (container *Container) ToDisk() (err error) { if err != nil { return } - return ioutil.WriteFile(container.jsonPath(), data, 0666) + err = ioutil.WriteFile(container.jsonPath(), data, 0666) + if err != nil { + return + } + return container.writeHostConfig() } -func (container *Container) ReadHostConfig() (*HostConfig, error) { +func (container *Container) readHostConfig() error { + container.hostConfig = &HostConfig{} + // If the hostconfig file does not exist, do not read it. + // (We still have to initialize container.hostConfig, + // but that's OK, since we just did that above.) + _, err := os.Stat(container.hostConfigPath()) + if os.IsNotExist(err) { + return nil + } data, err := ioutil.ReadFile(container.hostConfigPath()) if err != nil { - return &HostConfig{}, err + return err } - hostConfig := &HostConfig{} - if err := json.Unmarshal(data, hostConfig); err != nil { - return &HostConfig{}, err - } - return hostConfig, nil + return json.Unmarshal(data, container.hostConfig) } -func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) { - data, err := json.Marshal(hostConfig) +func (container *Container) writeHostConfig() (err error) { + data, err := json.Marshal(container.hostConfig) if err != nil { return } @@ -456,21 +468,13 @@ func (container *Container) generateEnvConfig(env []string) error { return nil } -func (container *Container) generateLXCConfig(hostConfig *HostConfig) error { +func (container *Container) generateLXCConfig() error { fo, err := os.Create(container.lxcConfigPath()) if err != nil { return err } defer fo.Close() - if err := LxcTemplateCompiled.Execute(fo, container); err != nil { - return err - } - if hostConfig != nil { - if err := LxcHostConfigTemplateCompiled.Execute(fo, hostConfig); err != nil { - return err - } - } - return nil + return LxcTemplateCompiled.Execute(fo, container) } func (container *Container) startPty() error { @@ -665,7 +669,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s }) } -func (container *Container) Start(hostConfig *HostConfig) (err error) { +func (container *Container) Start() (err error) { container.State.Lock() defer container.State.Unlock() defer func() { @@ -674,10 +678,6 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { } }() - if hostConfig == nil { // in docker start of docker restart we want to reuse previous HostConfigFile - hostConfig, _ = container.ReadHostConfig() - } - if container.State.Running { return fmt.Errorf("The container %s is already running.", container.ID) } @@ -687,7 +687,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { if container.runtime.networkManager.disabled { container.Config.NetworkDisabled = true } else { - if err := container.allocateNetwork(hostConfig); err != nil { + if err := container.allocateNetwork(); err != nil { return err } } @@ -711,7 +711,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { // Define illegal container destinations illegalDsts := []string{"/", "."} - for _, bind := range hostConfig.Binds { + for _, bind := range container.hostConfig.Binds { // FIXME: factorize bind parsing in parseBind var src, dst, mode string arr := strings.Split(bind, ":") @@ -845,7 +845,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { } } - if err := container.generateLXCConfig(hostConfig); err != nil { + if err := container.generateLXCConfig(); err != nil { return err } @@ -942,8 +942,11 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { params = append(params, "--", container.Path) params = append(params, container.Args...) - container.cmd = exec.Command("lxc-start", params...) - + var lxcStart string = "lxc-start" + if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor { + lxcStart = path.Join(container.runtime.config.GraphPath, "lxc-start-unconfined") + } + container.cmd = exec.Command(lxcStart, params...) // Setup logging of stdout and stderr to disk if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { return err @@ -970,8 +973,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { container.waitLock = make(chan struct{}) container.ToDisk() - container.SaveHostConfig(hostConfig) - go container.monitor(hostConfig) + go container.monitor() defer utils.Debugf("Container running: %v", container.State.Running) // We wait for the container to be fully running. @@ -1008,7 +1010,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) { } func (container *Container) Run() error { - if err := container.Start(&HostConfig{}); err != nil { + if err := container.Start(); err != nil { return err } container.Wait() @@ -1021,8 +1023,7 @@ func (container *Container) Output() (output []byte, err error) { return nil, err } defer pipe.Close() - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { return nil, err } output, err = ioutil.ReadAll(pipe) @@ -1054,7 +1055,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) { return utils.NewBufReader(reader), nil } -func (container *Container) allocateNetwork(hostConfig *HostConfig) error { +func (container *Container) allocateNetwork() error { if container.Config.NetworkDisabled { return nil } @@ -1083,11 +1084,11 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error { if container.Config.PortSpecs != nil { utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", ")) - if err := migratePortMappings(container.Config, hostConfig); err != nil { + if err := migratePortMappings(container.Config, container.hostConfig); err != nil { return err } container.Config.PortSpecs = nil - if err := container.SaveHostConfig(hostConfig); err != nil { + if err := container.writeHostConfig(); err != nil { return err } } @@ -1099,8 +1100,8 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error { if container.Config.ExposedPorts != nil { portSpecs = container.Config.ExposedPorts } - if hostConfig.PortBindings != nil { - bindings = hostConfig.PortBindings + if container.hostConfig.PortBindings != nil { + bindings = container.hostConfig.PortBindings } } else { if container.NetworkSettings.Ports != nil { @@ -1130,7 +1131,7 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error { } bindings[port] = binding } - container.SaveHostConfig(hostConfig) + container.writeHostConfig() container.NetworkSettings.Ports = bindings container.network = iface @@ -1166,7 +1167,7 @@ func (container *Container) waitLxc() error { } } -func (container *Container) monitor(hostConfig *HostConfig) { +func (container *Container) monitor() { // Wait for the program to exit // If the command does not exist, try to wait via lxc @@ -1323,11 +1324,7 @@ func (container *Container) Restart(seconds int) error { if err := container.Stop(seconds); err != nil { return err } - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { - return err - } - return nil + return container.Start() } // Wait blocks until the container stops running, then returns its exit code. diff --git a/container_test.go b/container_test.go index 69495d6d8..50942c02d 100644 --- a/container_test.go +++ b/container_test.go @@ -40,7 +40,7 @@ func TestIDFormat(t *testing.T) { func TestMultipleAttachRestart(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, hostConfig, _ := mkContainer( + container, _ := mkContainer( runtime, []string{"_", "/bin/sh", "-c", "i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"}, t, @@ -61,7 +61,7 @@ func TestMultipleAttachRestart(t *testing.T) { if err != nil { t.Fatal(err) } - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } l1, err := bufio.NewReader(stdout1).ReadString('\n') @@ -102,7 +102,7 @@ func TestMultipleAttachRestart(t *testing.T) { if err != nil { t.Fatal(err) } - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -136,7 +136,7 @@ func TestDiff(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) // Create a container and remove a file - container1, _, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t) + container1, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t) defer runtime.Destroy(container1) // The changelog should be empty and not fail before run. See #1705 @@ -178,7 +178,7 @@ func TestDiff(t *testing.T) { } // Create a new container from the commited image - container2, _, _ := mkContainer(runtime, []string{img.ID, "cat", "/etc/passwd"}, t) + container2, _ := mkContainer(runtime, []string{img.ID, "cat", "/etc/passwd"}, t) defer runtime.Destroy(container2) if err := container2.Run(); err != nil { @@ -197,7 +197,7 @@ func TestDiff(t *testing.T) { } // Create a new container - container3, _, _ := mkContainer(runtime, []string{"_", "rm", "/bin/httpd"}, t) + container3, _ := mkContainer(runtime, []string{"_", "rm", "/bin/httpd"}, t) defer runtime.Destroy(container3) if err := container3.Run(); err != nil { @@ -223,7 +223,7 @@ func TestDiff(t *testing.T) { func TestCommitAutoRun(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container1, _, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t) + container1, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t) defer runtime.Destroy(container1) if container1.State.Running { @@ -246,7 +246,7 @@ func TestCommitAutoRun(t *testing.T) { } // FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world - container2, hostConfig, _ := mkContainer(runtime, []string{img.ID}, t) + container2, _ := mkContainer(runtime, []string{img.ID}, t) defer runtime.Destroy(container2) stdout, err := container2.StdoutPipe() if err != nil { @@ -256,7 +256,7 @@ func TestCommitAutoRun(t *testing.T) { if err != nil { t.Fatal(err) } - if err := container2.Start(hostConfig); err != nil { + if err := container2.Start(); err != nil { t.Fatal(err) } container2.Wait() @@ -283,7 +283,7 @@ func TestCommitRun(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container1, hostConfig, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t) + container1, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t) defer runtime.Destroy(container1) if container1.State.Running { @@ -306,7 +306,7 @@ func TestCommitRun(t *testing.T) { } // FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world - container2, hostConfig, _ := mkContainer(runtime, []string{img.ID, "cat", "/world"}, t) + container2, _ := mkContainer(runtime, []string{img.ID, "cat", "/world"}, t) defer runtime.Destroy(container2) stdout, err := container2.StdoutPipe() if err != nil { @@ -316,7 +316,7 @@ func TestCommitRun(t *testing.T) { if err != nil { t.Fatal(err) } - if err := container2.Start(hostConfig); err != nil { + if err := container2.Start(); err != nil { t.Fatal(err) } container2.Wait() @@ -342,7 +342,7 @@ func TestCommitRun(t *testing.T) { func TestStart(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, hostConfig, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t) + container, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t) defer runtime.Destroy(container) cStdin, err := container.StdinPipe() @@ -350,7 +350,7 @@ func TestStart(t *testing.T) { t.Fatal(err) } - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -360,7 +360,7 @@ func TestStart(t *testing.T) { if !container.State.Running { t.Errorf("Container should be running") } - if err := container.Start(hostConfig); err == nil { + if err := container.Start(); err == nil { t.Fatalf("A running container should be able to be started") } @@ -372,7 +372,7 @@ func TestStart(t *testing.T) { func TestRun(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) + container, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container) if container.State.Running { @@ -452,7 +452,7 @@ func TestKillDifferentUser(t *testing.T) { if container.State.Running { t.Errorf("Container shouldn't be running") } - if err := container.Start(&HostConfig{}); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -501,7 +501,8 @@ func TestCreateVolume(t *testing.T) { t.Fatal(err) } defer runtime.Destroy(c) - if err := c.Start(hc); err != nil { + c.hostConfig = hc + if err := c.Start(); err != nil { t.Fatal(err) } c.WaitTimeout(500 * time.Millisecond) @@ -525,8 +526,7 @@ func TestKill(t *testing.T) { if container.State.Running { t.Errorf("Container shouldn't be running") } - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } @@ -642,8 +642,7 @@ func TestRestartStdin(t *testing.T) { if err != nil { t.Fatal(err) } - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } if _, err := io.WriteString(stdin, "hello world"); err != nil { @@ -673,7 +672,7 @@ func TestRestartStdin(t *testing.T) { if err != nil { t.Fatal(err) } - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } if _, err := io.WriteString(stdin, "hello world #2"); err != nil { @@ -850,11 +849,10 @@ func TestMultipleContainers(t *testing.T) { defer runtime.Destroy(container2) // Start both containers - hostConfig := &HostConfig{} - if err := container1.Start(hostConfig); err != nil { + if err := container1.Start(); err != nil { t.Fatal(err) } - if err := container2.Start(hostConfig); err != nil { + if err := container2.Start(); err != nil { t.Fatal(err) } @@ -904,8 +902,7 @@ func TestStdin(t *testing.T) { if err != nil { t.Fatal(err) } - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } defer stdin.Close() @@ -950,8 +947,7 @@ func TestTty(t *testing.T) { if err != nil { t.Fatal(err) } - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } defer stdin.Close() @@ -992,8 +988,7 @@ func TestEnv(t *testing.T) { t.Fatal(err) } defer stdout.Close() - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { t.Fatal(err) } container.Wait() @@ -1121,7 +1116,7 @@ func TestLXCConfig(t *testing.T) { t.Fatal(err) } defer runtime.Destroy(container) - container.generateLXCConfig(nil) + container.generateLXCConfig() grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar") grepFile(t, container.lxcConfigPath(), fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem)) @@ -1144,7 +1139,7 @@ func TestCustomLxcConfig(t *testing.T) { t.Fatal(err) } defer runtime.Destroy(container) - hostConfig := &HostConfig{LxcConf: []KeyValuePair{ + container.hostConfig = &HostConfig{LxcConf: []KeyValuePair{ { Key: "lxc.utsname", Value: "docker", @@ -1155,7 +1150,7 @@ func TestCustomLxcConfig(t *testing.T) { }, }} - container.generateLXCConfig(hostConfig) + container.generateLXCConfig() grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker") grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1") } @@ -1208,8 +1203,7 @@ func BenchmarkRunParallel(b *testing.B) { return } defer runtime.Destroy(container) - hostConfig := &HostConfig{} - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { complete <- err return } @@ -1253,7 +1247,7 @@ func TestCopyVolumeUidGid(t *testing.T) { defer nuke(r) // Add directory not owned by root - container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t) + container1, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t) defer r.Destroy(container1) if container1.State.Running { @@ -1290,7 +1284,7 @@ func TestCopyVolumeContent(t *testing.T) { defer nuke(r) // Put some content in a directory of a container and commit it - container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t) + container1, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t) defer r.Destroy(container1) if container1.State.Running { @@ -1527,9 +1521,9 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { if err != nil { t.Fatal(err) } - defer runtime.Destroy(c) - if err := c.Start(hc); err != nil { + c.hostConfig = hc + if err := c.Start(); err != nil { t.Fatal(err) } c.WaitTimeout(500 * time.Millisecond) diff --git a/docs/sources/contributing/devenvironment.rst b/docs/sources/contributing/devenvironment.rst index fd6979166..fdbc6d7e1 100644 --- a/docs/sources/contributing/devenvironment.rst +++ b/docs/sources/contributing/devenvironment.rst @@ -56,7 +56,7 @@ To create the Docker binary, run this command: .. code-block:: bash - sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary + sudo docker run -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary This will create the Docker binary in ``./bundles/-dev/binary/`` @@ -64,18 +64,11 @@ This will create the Docker binary in ``./bundles/-dev/binary/`` Step 5: Run the Tests --------------------- -To run the Docker test cases you first need to disable `AppArmor `_ using the following commands - -.. code-block:: bash - - sudo /etc/init.d/apparmor stop - sudo /etc/init.d/apparmor teardown - To execute the test cases, run this command: .. code-block:: bash - sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test + sudo docker run -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test Note: if you're running the tests in vagrant, you need to specify a dns entry in the command: `-dns 8.8.8.8` diff --git a/hack/dind b/hack/dind index 17e7dfa70..eff656b0e 100755 --- a/hack/dind +++ b/hack/dind @@ -21,6 +21,14 @@ mountpoint -q $CGROUP || exit 1 } +if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security +then + mount -t securityfs none /sys/kernel/security || { + echo "Could not mount /sys/kernel/security." + echo "AppArmor detection and -privileged mode might break." + } +fi + # Mount the cgroup hierarchies exactly as they are in the parent system. for SUBSYS in $(cut -d: -f2 /proc/1/cgroup) do diff --git a/hack/infrastructure/docker-ci/deployment.py b/hack/infrastructure/docker-ci/deployment.py index f03db06b3..655bdd43b 100755 --- a/hack/infrastructure/docker-ci/deployment.py +++ b/hack/infrastructure/docker-ci/deployment.py @@ -135,11 +135,6 @@ sudo('curl -s https://phantomjs.googlecode.com/files/' 'phantomjs-1.9.1-linux-x86_64.tar.bz2 | tar jx -C /usr/bin' ' --strip-components=2 phantomjs-1.9.1-linux-x86_64/bin/phantomjs') -#### FIXME. Temporarily install docker with proper apparmor handling -sudo('stop docker') -sudo('wget -q -O /usr/bin/docker http://test.docker.io/test/docker') -sudo('start docker') - # Preventively reboot docker-ci daily sudo('ln -s /sbin/reboot /etc/cron.daily') diff --git a/hack/infrastructure/docker-ci/nightlyrelease/Dockerfile b/hack/infrastructure/docker-ci/nightlyrelease/Dockerfile index 8eb0b7211..541f3a958 100644 --- a/hack/infrastructure/docker-ci/nightlyrelease/Dockerfile +++ b/hack/infrastructure/docker-ci/nightlyrelease/Dockerfile @@ -11,7 +11,7 @@ # "GPG_PASSPHRASE='Test_docker_GPG_passphrase_signature' # "INDEX_AUTH='Encripted_index_authentication' } # TO_BUILD: docker build -t dockerbuilder . -# TO_RELEASE: docker run -i -t -privileged -lxc-conf="lxc.aa_profile = unconfined" -e AWS_S3_BUCKET="test.docker.io" dockerbuilder +# TO_RELEASE: docker run -i -t -privileged -e AWS_S3_BUCKET="test.docker.io" dockerbuilder from docker maintainer Daniel Mizyrycki @@ -23,9 +23,6 @@ run apt-get update; apt-get install -y -q wget python2.7 # Add production docker binary run wget -q -O /usr/bin/docker http://get.docker.io/builds/Linux/x86_64/docker-latest; chmod +x /usr/bin/docker -#### FIXME. Temporarily install docker with proper apparmor handling -run wget -q -O /usr/bin/docker http://test.docker.io/test/docker; chmod +x /usr/bin/docker - # Add proto docker builder add ./dockerbuild /usr/bin/dockerbuild run chmod +x /usr/bin/dockerbuild diff --git a/hack/infrastructure/docker-ci/nightlyrelease/dockerbuild b/hack/infrastructure/docker-ci/nightlyrelease/dockerbuild index 76f8a69b8..83a7157a3 100644 --- a/hack/infrastructure/docker-ci/nightlyrelease/dockerbuild +++ b/hack/infrastructure/docker-ci/nightlyrelease/dockerbuild @@ -13,9 +13,6 @@ cd / git clone -q http://github.com/dotcloud/docker /go/src/github.com/dotcloud/docker cd /go/src/github.com/dotcloud/docker -echo FIXME. Temporarily skip TestPrivilegedCanMount until DinD works reliable on AWS -git pull -q https://github.com/mzdaniel/docker.git dind-aws || exit 1 - # Launch docker daemon using dind inside the container ./hack/dind /usr/bin/docker -d & sleep 5 @@ -27,7 +24,7 @@ date > timestamp docker build -t docker . # Run Docker unittests binary and Ubuntu package -docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh +docker run -privileged docker hack/make.sh exit_status=$? # Display load if test fails diff --git a/lxc_template.go b/lxc_template.go index d6da584ae..aacca0b01 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -11,7 +11,6 @@ lxc.utsname = {{.Config.Hostname}} {{else}} lxc.utsname = {{.Id}} {{end}} -#lxc.aa_profile = unconfined {{if .Config.NetworkDisabled}} # network is disabled (-n=false) @@ -46,7 +45,7 @@ lxc.console = none # no controlling tty at all lxc.tty = 1 -{{if .Config.Privileged}} +{{if (getHostConfig .).Privileged}} lxc.cgroup.devices.allow = a {{else}} # no implicit access to devices @@ -66,7 +65,7 @@ lxc.cgroup.devices.allow = c 4:1 rwm lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 1:8 rwm -# /dev/pts/* - pts namespaces are "coming soon" +# /dev/pts/ - pts namespaces are "coming soon" lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm @@ -109,8 +108,13 @@ lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,{{ if ind {{end}} {{end}} -{{if .Config.Privileged}} +{{if (getHostConfig .).Privileged}} # retain all capabilities; no lxc.cap.drop line +{{if (getCapabilities .).AppArmor}} +lxc.aa_profile = unconfined +{{else}} +#lxc.aa_profile = unconfined +{{end}} {{else}} # drop linux capabilities (apply mainly to the user root in the container) # (Note: 'lxc.cap.keep' is coming soon and should replace this under the @@ -130,18 +134,15 @@ lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}} {{if .Config.CpuShares}} lxc.cgroup.cpu.shares = {{.Config.CpuShares}} {{end}} -` -const LxcHostConfigTemplate = ` -{{if .LxcConf}} -{{range $pair := .LxcConf}} +{{if (getHostConfig .).LxcConf}} +{{range $pair := (getHostConfig .).LxcConf}} {{$pair.Key}} = {{$pair.Value}} {{end}} {{end}} ` var LxcTemplateCompiled *template.Template -var LxcHostConfigTemplateCompiled *template.Template func getMemorySwap(config *Config) int64 { // By default, MemorySwap is set to twice the size of RAM. @@ -152,17 +153,23 @@ func getMemorySwap(config *Config) int64 { return config.Memory * 2 } +func getHostConfig(container *Container) *HostConfig { + return container.hostConfig +} + +func getCapabilities(container *Container) *Capabilities { + return container.runtime.capabilities +} + func init() { var err error funcMap := template.FuncMap{ - "getMemorySwap": getMemorySwap, + "getMemorySwap": getMemorySwap, + "getHostConfig": getHostConfig, + "getCapabilities": getCapabilities, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) if err != nil { panic(err) } - LxcHostConfigTemplateCompiled, err = template.New("lxc-hostconfig").Funcs(funcMap).Parse(LxcHostConfigTemplate) - if err != nil { - panic(err) - } } diff --git a/runtime.go b/runtime.go index 6d5139390..ac510e4d3 100644 --- a/runtime.go +++ b/runtime.go @@ -24,6 +24,7 @@ type Capabilities struct { MemoryLimit bool SwapLimit bool IPv4ForwardingDisabled bool + AppArmor bool } type Runtime struct { @@ -149,8 +150,7 @@ func (runtime *Runtime) Register(container *Container) error { utils.Debugf("Restarting") container.State.Ghost = false container.State.setStopped(0) - hostConfig, _ := container.ReadHostConfig() - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { return err } nomonitor = true @@ -169,9 +169,7 @@ func (runtime *Runtime) Register(container *Container) error { if !container.State.Running { close(container.waitLock) } else if !nomonitor { - hostConfig, _ := container.ReadHostConfig() - container.allocateNetwork(hostConfig) - go container.monitor(hostConfig) + go container.monitor() } return nil } @@ -310,6 +308,15 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) { if runtime.capabilities.IPv4ForwardingDisabled && !quiet { log.Printf("WARNING: IPv4 forwarding is disabled.") } + + // Check if AppArmor seems to be enabled on this system. + if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { + utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.") + runtime.capabilities.AppArmor = false + } else { + utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.") + runtime.capabilities.AppArmor = true + } } // Create creates a new container from the given configuration with a given name. @@ -400,6 +407,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin Path: entrypoint, Args: args, //FIXME: de-duplicate from config Config: config, + hostConfig: &HostConfig{}, Image: img.ID, // Always use the resolved image id NetworkSettings: &NetworkSettings{}, // FIXME: do we need to store this in the container? @@ -574,6 +582,9 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { return nil, err } + if err := copyLxcStart(config.Root); err != nil { + return nil, err + } g, err := NewGraph(path.Join(config.Root, "graph")) if err != nil { return nil, err @@ -636,6 +647,27 @@ func (runtime *Runtime) Close() error { return runtime.containerGraph.Close() } +func copyLxcStart(root string) error { + sourcePath, err := exec.LookPath("lxc-start") + if err != nil { + return err + } + targetPath := path.Join(root, "lxc-start-unconfined") + sourceFile, err := os.Open(sourcePath) + if err != nil { + return err + } + defer sourceFile.Close() + targetFile, err := os.Create(targetPath) + if err != nil { + return err + } + defer targetFile.Close() + os.Chmod(targetPath, 0755) + _, err = io.Copy(targetFile, sourceFile) + return err +} + // History is a convenience type for storing a list of containers, // ordered by creation date. type History []*Container diff --git a/runtime_test.go b/runtime_test.go index 0c65188a3..392e442d3 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -325,13 +325,13 @@ func TestGet(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container1, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) + container1, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container1) - container2, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) + container2, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container2) - container3, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) + container3, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t) defer runtime.Destroy(container3) if runtime.Get(container1.ID) != container1 { @@ -390,13 +390,13 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container, t.Logf("Port %v already in use, trying another one", strPort) } - hostConfig := &HostConfig{ + container.hostConfig = &HostConfig{ PortBindings: make(map[Port][]PortBinding), } - hostConfig.PortBindings[p] = []PortBinding{ + container.hostConfig.PortBindings[p] = []PortBinding{ {}, } - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { nuke(runtime) t.Fatal(err) } @@ -503,16 +503,15 @@ func TestRestore(t *testing.T) { runtime1 := mkRuntime(t) defer nuke(runtime1) // Create a container with one instance of docker - container1, _, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t) + container1, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t) defer runtime1.Destroy(container1) // Create a second container meant to be killed - container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t) + container2, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t) defer runtime1.Destroy(container2) // Start the container non blocking - hostConfig := &HostConfig{} - if err := container2.Start(hostConfig); err != nil { + if err := container2.Start(); err != nil { t.Fatal(err) } @@ -575,25 +574,23 @@ func TestReloadContainerLinks(t *testing.T) { runtime1 := mkRuntime(t) defer nuke(runtime1) // Create a container with one instance of docker - container1, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/sh"}, t) + container1, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/sh"}, t) defer runtime1.Destroy(container1) // Create a second container meant to be killed - container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t) + container2, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t) defer runtime1.Destroy(container2) // Start the container non blocking - hostConfig := &HostConfig{} - if err := container2.Start(hostConfig); err != nil { + if err := container2.Start(); err != nil { t.Fatal(err) } - h1 := &HostConfig{} // Add a link to container 2 - h1.Links = []string{"/" + container2.ID + ":first"} + container1.hostConfig.Links = []string{"/" + container2.ID + ":first"} if err := runtime1.RegisterLink(container1, container2, "first"); err != nil { t.Fatal(err) } - if err := container1.Start(h1); err != nil { + if err := container1.Start(); err != nil { t.Fatal(err) } diff --git a/server.go b/server.go index cdf275122..a58daf54e 100644 --- a/server.go +++ b/server.go @@ -1304,7 +1304,7 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error { // After we load all the links into the runtime // set them to nil on the hostconfig hostConfig.Links = nil - if err := container.SaveHostConfig(hostConfig); err != nil { + if err := container.writeHostConfig(); err != nil { return err } } @@ -1317,8 +1317,11 @@ func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error { if container == nil { return fmt.Errorf("No such container: %s", name) } - - if err := container.Start(hostConfig); err != nil { + if hostConfig != nil { + container.hostConfig = hostConfig + container.ToDisk() + } + if err := container.Start(); err != nil { return fmt.Errorf("Cannot start container %s: %s", name, err) } srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image)) diff --git a/server_test.go b/server_test.go index 7a8ea4e41..7f9cbaadf 100644 --- a/server_test.go +++ b/server_test.go @@ -246,14 +246,14 @@ func TestContainerTop(t *testing.T) { srv := &Server{runtime: runtime} - c, hostConfig, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t) - c, hostConfig, err := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t) + c, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t) + c, err := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t) if err != nil { t.Fatal(err) } defer runtime.Destroy(c) - if err := c.Start(hostConfig); err != nil { + if err := c.Start(); err != nil { t.Fatal(err) } diff --git a/utils_test.go b/utils_test.go index d74761491..14cbd7c6b 100644 --- a/utils_test.go +++ b/utils_test.go @@ -116,7 +116,7 @@ func readFile(src string, t *testing.T) (content string) { // dynamically replaced by the current test image. // The caller is responsible for destroying the container. // Call t.Fatal() at the first error. -func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig, error) { +func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, error) { config, hostConfig, _, err := ParseRun(args, nil) defer func() { if err != nil && t != nil { @@ -124,16 +124,17 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf } }() if err != nil { - return nil, nil, err + return nil, err } if config.Image == "_" { config.Image = GetTestImage(r).ID } c, _, err := r.Create(config, "") if err != nil { - return nil, nil, err + return nil, err } - return c, hostConfig, nil + c.hostConfig = hostConfig + return c, nil } // Create a test container, start it, wait for it to complete, destroy it, @@ -146,7 +147,7 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e t.Fatal(err) } }() - container, hostConfig, err := mkContainer(r, args, t) + container, err := mkContainer(r, args, t) if err != nil { return "", err } @@ -156,7 +157,7 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e return "", err } defer stdout.Close() - if err := container.Start(hostConfig); err != nil { + if err := container.Start(); err != nil { return "", err } container.Wait() From f9901ead069671c04a2f99c83c1e4d8a13597e7b Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 31 Oct 2013 15:17:08 -0700 Subject: [PATCH 18/71] use a symlink instead of a copy --- runtime.go | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/runtime.go b/runtime.go index ac510e4d3..7fb673f43 100644 --- a/runtime.go +++ b/runtime.go @@ -582,7 +582,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { return nil, err } - if err := copyLxcStart(config.Root); err != nil { + if err := linkLxcStart(config.Root); err != nil { return nil, err } g, err := NewGraph(path.Join(config.Root, "graph")) @@ -647,25 +647,21 @@ func (runtime *Runtime) Close() error { return runtime.containerGraph.Close() } -func copyLxcStart(root string) error { +func linkLxcStart(root string) error { sourcePath, err := exec.LookPath("lxc-start") if err != nil { return err } targetPath := path.Join(root, "lxc-start-unconfined") - sourceFile, err := os.Open(sourcePath) - if err != nil { + + if _, err := os.Stat(targetPath); err != nil && !os.IsNotExist(err) { return err + } else if err == nil { + if err := os.Remove(targetPath); err != nil { + return err + } } - defer sourceFile.Close() - targetFile, err := os.Create(targetPath) - if err != nil { - return err - } - defer targetFile.Close() - os.Chmod(targetPath, 0755) - _, err = io.Copy(targetFile, sourceFile) - return err + return os.Symlink(sourcePath, targetPath) } // History is a convenience type for storing a list of containers, From dcf4633e95c7f3784cb29b9c1f9510af28b882ed Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 1 Nov 2013 14:00:02 -0700 Subject: [PATCH 19/71] Fixed 127.0.0.0 typo in cli.rst --- docs/sources/commandline/cli.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index c1f76d056..8f778c32d 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -637,7 +637,7 @@ using the container, but inside the current working directory. .. code-block:: bash - docker run -p 127.0.0.0:80:8080 ubuntu bash + docker run -p 127.0.0.1:80:8080 ubuntu bash This binds port ``8080`` of the container to port ``80`` on 127.0.0.1 of the host machine. :ref:`port_redirection` explains in detail how to manipulate ports From 7ddd4fe66a00266507848e8ebc1ce3782532b51d Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 1 Nov 2013 14:01:32 -0700 Subject: [PATCH 20/71] rebase --- container.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container.go b/container.go index ea34f6c68..2e52c9532 100644 --- a/container.go +++ b/container.go @@ -944,7 +944,7 @@ func (container *Container) Start() (err error) { var lxcStart string = "lxc-start" if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor { - lxcStart = path.Join(container.runtime.config.GraphPath, "lxc-start-unconfined") + lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined") } container.cmd = exec.Command(lxcStart, params...) // Setup logging of stdout and stderr to disk @@ -1116,7 +1116,7 @@ func (container *Container) allocateNetwork() error { for port := range portSpecs { binding := bindings[port] - if hostConfig.PublishAllPorts && len(binding) == 0 { + if container.hostConfig.PublishAllPorts && len(binding) == 0 { binding = append(binding, PortBinding{}) } for i := 0; i < len(binding); i++ { From f3685333c0fdea58c8e6adbc93c95ebbc4a9fa47 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 1 Nov 2013 15:46:26 -0700 Subject: [PATCH 21/71] fix regression in /etc/hosts --- container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.go b/container.go index 2e52c9532..a426b2fd0 100644 --- a/container.go +++ b/container.go @@ -304,7 +304,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } config := &Config{ - Hostname: *flHostname, + Hostname: hostname, Domainname: domainname, PortSpecs: nil, // Deprecated ExposedPorts: ports, From c33d26a8da0dc2dd33918bc5e21283dd62f6d755 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 2 Nov 2013 17:41:35 -0700 Subject: [PATCH 22/71] Some format and syntax changes. * Added sudo as per convention to docker commands * Break the Dockerfile block up * Redis is a proper noun * Minor whitespace fixes --- AUTHORS | 1 + docs/sources/examples/linking_into_redis.rst | 45 +++++++++++--------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/AUTHORS b/AUTHORS index 579ee0c48..d3cf1dcae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -75,6 +75,7 @@ Hector Castro Hunter Blanks Isao Jonas James Carr +James Turnbull Jason McVetta Jean-Baptiste Barth Jeff Lindsay diff --git a/docs/sources/examples/linking_into_redis.rst b/docs/sources/examples/linking_into_redis.rst index d6a42dde7..1b91b8451 100644 --- a/docs/sources/examples/linking_into_redis.rst +++ b/docs/sources/examples/linking_into_redis.rst @@ -9,12 +9,14 @@ Linking Redis .. include:: example_header.inc -Building a redis container to link as a child of our web application. +Building a Redis container to link as a child of our web application. -Building the redis container +Building the Redis container ---------------------------- -Lets build a redis image with the following Dockerfile. +Lets build a Redis image with the following Dockerfile. + +First checkout the Redis source code. .. code-block:: bash @@ -22,7 +24,10 @@ Lets build a redis image with the following Dockerfile. cd redis git checkout 2.6 - # Save this Dockerfile to the root of the redis repository. + +Now let's create a Dockerfile in the root of the Redis repository. + +.. code-block:: bash # Build redis from source # Make sure you have the redis source code checked out in @@ -51,37 +56,37 @@ Lets build a redis image with the following Dockerfile. We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports -to connect to our redis container on. If you do not expose any ports for the +to connect to our Redis container on. If you do not expose any ports for the image then docker will not be able to establish the link between containers. -Run the redis container +Run the Redis container ----------------------- .. code-block:: bash - - docker run -d -e PASSWORD=docker -name redis redis-2.6 --requirepass docker - -This will run our redis container with the password docker + + sudo docker run -d -e PASSWORD=docker -name redis redis-2.6 --requirepass docker + +This will run our Redis container with the password docker to secure our service. By specifying the ``-name`` flag on run -we will assign the name ``redis`` to this container. If we do not specify a name for +we will assign the name ``redis`` to this container. If we do not specify a name for our container via the ``-name`` flag docker will automatically generate a name for us. We can issue all the commands that you would expect; start, stop, attach, using the name for our container. The name also allows us to link other containers into this one. -Linking redis as a child +Linking Redis as a child ------------------------ -Next we can start a new web application that has a dependency on redis and apply a link -to connect both containers. If you noticed when running our redis server we did not use -the ``-p`` flag to publish the redis port to the host system. Redis exposed port 6379 via the Dockerfile +Next we can start a new web application that has a dependency on Redis and apply a link +to connect both containers. If you noticed when running our Redis server we did not use +the ``-p`` flag to publish the Redis port to the host system. Redis exposed port 6379 via the Dockerfile and this is all we need to establish a link. -Now lets start our web application with a link into redis. +Now let's start our web application with a link into Redis. .. code-block:: bash - - docker run -t -i -link redis:db -name webapp ubuntu bash + + sudo docker run -t -i -link redis:db -name webapp ubuntu bash root@4c01db0b339c:/# env @@ -105,7 +110,7 @@ Now lets start our web application with a link into redis. When we inspect the environment of the linked container we can see a few extra environment variables have been added. When you specified ``-link redis:db`` you are telling docker -to link the container named ``redis`` into this new container with the alias ``db``. +to link the container named ``redis`` into this new container with the alias ``db``. Environment variables are prefixed with the alias so that the parent container can access network and environment information from the containers that are linked into it. @@ -128,5 +133,5 @@ network and environment information from the containers that are linked into it. Accessing the network information along with the environment of the child container allows -us to easily connect to the redis service on the specific ip and port and use the password +us to easily connect to the Redis service on the specific IP and port and use the password specified in the environment. From 1cb4557cc8e40f509898bded6684a45070fbb2a4 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 2 Nov 2013 18:26:52 -0700 Subject: [PATCH 23/71] A variety of syntax and style fixes for the Docker examples * Made Docker a proper noun when needed. * Fixed code syntax * Fixed some whitespace issues * Fixed some typos and grammar * Tidied up some examples --- .../sources/examples/couchdb_data_volumes.rst | 10 ++--- docs/sources/examples/hello_world.rst | 19 ++++----- docs/sources/examples/index.rst | 2 +- docs/sources/examples/mongodb.rst | 18 ++++----- docs/sources/examples/nodejs_web_app.rst | 32 +++++++-------- docs/sources/examples/postgresql_service.rst | 9 ++--- docs/sources/examples/python_web_app.rst | 40 +++++++++---------- .../examples/running_redis_service.rst | 12 +++--- .../sources/examples/running_riak_service.rst | 2 +- docs/sources/examples/running_ssh_service.rst | 13 +++--- 10 files changed, 79 insertions(+), 78 deletions(-) diff --git a/docs/sources/examples/couchdb_data_volumes.rst b/docs/sources/examples/couchdb_data_volumes.rst index 7296a3eea..60674fe9a 100644 --- a/docs/sources/examples/couchdb_data_volumes.rst +++ b/docs/sources/examples/couchdb_data_volumes.rst @@ -10,7 +10,7 @@ CouchDB Service .. include:: example_header.inc Here's an example of using data volumes to share the same data between -2 CouchDB containers. This could be used for hot upgrades, testing +two CouchDB containers. This could be used for hot upgrades, testing different versions of CouchDB on the same data, etc. Create first database @@ -25,8 +25,8 @@ Note that we're marking ``/var/lib/couchdb`` as a data volume. Add data to the first database ------------------------------ -We're assuming your docker host is reachable at `localhost`. If not, -replace `localhost` with the public IP of your docker host. +We're assuming your Docker host is reachable at ``localhost``. If not, +replace ``localhost`` with the public IP of your Docker host. .. code-block:: bash @@ -37,7 +37,7 @@ replace `localhost` with the public IP of your docker host. Create second database ---------------------- -This time, we're requesting shared access to $COUCH1's volumes. +This time, we're requesting shared access to ``$COUCH1``'s volumes. .. code-block:: bash @@ -52,5 +52,5 @@ Browse data on the second database URL="http://$HOST:$(sudo docker port $COUCH2 5984)/_utils/" echo "Navigate to $URL in your browser. You should see the same data as in the first database"'!' -Congratulations, you are running 2 Couchdb containers, completely +Congratulations, you are now running two Couchdb containers, completely isolated from each other *except* for their data. diff --git a/docs/sources/examples/hello_world.rst b/docs/sources/examples/hello_world.rst index 54ebdacc6..6d6c1b28a 100644 --- a/docs/sources/examples/hello_world.rst +++ b/docs/sources/examples/hello_world.rst @@ -12,16 +12,16 @@ Hello World Running the Examples ==================== -All the examples assume your machine is running the docker daemon. To -run the docker daemon in the background, simply type: +All the examples assume your machine is running the ``docker`` daemon. To +run the ``docker`` daemon in the background, simply type: .. code-block:: bash sudo docker -d & -Now you can run docker in client mode: by default all commands will be +Now you can run Docker in client mode: by default all commands will be forwarded to the ``docker`` daemon via a protected Unix socket, so you -must run as root. +must run as the ``root`` or via the ``sudo`` command. .. code-block:: bash @@ -38,23 +38,24 @@ Hello World This is the most basic example available for using Docker. -Download the base image (named "ubuntu"): +Download the base image which is named ``ubuntu``: .. code-block:: bash # Download an ubuntu image sudo docker pull ubuntu -Alternatively to the *ubuntu* image, you can select *busybox*, a bare +Alternatively to the ``ubuntu`` image, you can select ``busybox``, a bare minimal Linux system. The images are retrieved from the Docker repository. .. code-block:: bash - #run a simple echo command, that will echo hello world back to the console over standard out. sudo docker run ubuntu /bin/echo hello world +This command will run a simple ``echo`` command, that will echo ``hello world`` back to the console over standard out. + **Explanation:** - **"sudo"** execute the following commands as user *root* @@ -100,9 +101,9 @@ we stop it. CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done") We are going to run a simple hello world daemon in a new container -made from the *ubuntu* image. +made from the ``ubuntu`` image. -- **"docker run -d "** run a command in a new container. We pass "-d" +- **"sudo docker run -d "** run a command in a new container. We pass "-d" so it runs as a daemon. - **"ubuntu"** is the image we want to run the command inside of. - **"/bin/sh -c"** is the command we want to run in the container diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst index 74a7e7c40..6167e3565 100644 --- a/docs/sources/examples/index.rst +++ b/docs/sources/examples/index.rst @@ -10,7 +10,7 @@ Examples Here are some examples of how to use Docker to create running processes, starting from a very simple *Hello World* and progressing -to more substantial services like you might find in production. +to more substantial services like those which you might find in production. .. toctree:: :maxdepth: 1 diff --git a/docs/sources/examples/mongodb.rst b/docs/sources/examples/mongodb.rst index 5527fc00c..3e37d74c3 100644 --- a/docs/sources/examples/mongodb.rst +++ b/docs/sources/examples/mongodb.rst @@ -10,8 +10,8 @@ Building an Image with MongoDB .. include:: example_header.inc The goal of this example is to show how you can build your own -docker images with MongoDB preinstalled. We will do that by -constructing a Dockerfile that downloads a base image, adds an +Docker images with MongoDB pre-installed. We will do that by +constructing a ``Dockerfile`` that downloads a base image, adds an apt source and installs the database software on Ubuntu. Creating a ``Dockerfile`` @@ -41,7 +41,7 @@ Since we want to be running the latest version of MongoDB we'll need to add the RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/10gen.list Then, we don't want Ubuntu to complain about init not being available so we'll -divert /sbin/initctl to /bin/true so it thinks everything is working. +divert ``/sbin/initctl`` to ``/bin/true`` so it thinks everything is working. .. code-block:: bash @@ -65,8 +65,8 @@ run without needing to provide a special configuration file) # Create the MongoDB data directory RUN mkdir -p /data/db -Finally, we'll expose the standard port that MongoDB runs on (27107) as well as -define an ENTRYPOINT for the container. +Finally, we'll expose the standard port that MongoDB runs on, 27107, as well as +define an ``ENTRYPOINT`` instruction for the container. .. code-block:: bash @@ -78,7 +78,7 @@ run all of the commands. .. code-block:: bash - docker build -t /mongodb . + sudo docker build -t /mongodb . Now you should be able to run ``mongod`` as a daemon and be able to connect on the local port! @@ -86,13 +86,13 @@ the local port! .. code-block:: bash # Regular style - MONGO_ID=$(docker run -d /mongodb) + MONGO_ID=$(sudo docker run -d /mongodb) # Lean and mean - MONGO_ID=$(docker run -d /mongodb --noprealloc --smallfiles) + MONGO_ID=$(sudo docker run -d /mongodb --noprealloc --smallfiles) # Check the logs out - docker logs $MONGO_ID + sudo docker logs $MONGO_ID # Connect and play around mongo --port diff --git a/docs/sources/examples/nodejs_web_app.rst b/docs/sources/examples/nodejs_web_app.rst index 67584d179..19b10f6cb 100644 --- a/docs/sources/examples/nodejs_web_app.rst +++ b/docs/sources/examples/nodejs_web_app.rst @@ -10,7 +10,7 @@ Node.js Web App .. include:: example_header.inc The goal of this example is to show you how you can build your own -docker images from a parent image using a ``Dockerfile`` . We will do +Docker images from a parent image using a ``Dockerfile`` . We will do that by making a simple Node.js hello world web application running on CentOS. You can get the full source code at https://github.com/gasi/docker-node-hello. @@ -55,7 +55,7 @@ Then, create an ``index.js`` file that defines a web app using the In the next steps, we’ll look at how you can run this app inside a CentOS -container using docker. First, you’ll need to build a docker image of your app. +container using Docker. First, you’ll need to build a Docker image of your app. Creating a ``Dockerfile`` +++++++++++++++++++++++++ @@ -67,8 +67,8 @@ Create an empty file called ``Dockerfile``: touch Dockerfile Open the ``Dockerfile`` in your favorite text editor and add the following line -that defines the version of docker the image requires to build -(this example uses docker 0.3.4): +that defines the version of Docker the image requires to build +(this example uses Docker 0.3.4): .. code-block:: bash @@ -76,7 +76,7 @@ that defines the version of docker the image requires to build Next, define the parent image you want to use to build your own image on top of. Here, we’ll use `CentOS `_ (tag: ``6.4``) -available on the `docker index`_: +available on the `Docker index`_: .. code-block:: bash @@ -95,23 +95,23 @@ To install the right package for CentOS, we’ll use the instructions from the # Install Node.js and npm RUN yum install -y npm -To bundle your app’s source code inside the docker image, use the ``ADD`` -command: +To bundle your app’s source code inside the Docker image, use the ``ADD`` +instruction: .. code-block:: bash # Bundle app source ADD . /src -Install your app dependencies using npm: +Install your app dependencies using the ``npm`` binary: .. code-block:: bash # Install app dependencies RUN cd /src; npm install -Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` command -to have it mapped by the docker daemon: +Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` instruction +to have it mapped by the ``docker`` daemon: .. code-block:: bash @@ -152,7 +152,7 @@ Building your image +++++++++++++++++++ Go to the directory that has your ``Dockerfile`` and run the following -command to build a docker image. The ``-t`` flag let’s you tag your +command to build a Docker image. The ``-t`` flag let’s you tag your image so it’s easier to find later using the ``docker images`` command: @@ -160,7 +160,7 @@ command: sudo docker build -t /centos-node-hello . -Your image will now be listed by docker: +Your image will now be listed by Docker: .. code-block:: bash @@ -199,17 +199,17 @@ Print the output of your app: Test ++++ -To test your app, get the the port of your app that docker mapped: +To test your app, get the the port of your app that Docker mapped: .. code-block:: bash - docker ps + sudo docker ps > # Example > ID IMAGE COMMAND ... PORTS > ecce33b30ebf gasi/centos-node-hello:latest node /src/index.js 49160->8080 -In the example above, docker mapped the ``8080`` port of the container to +In the example above, Docker mapped the ``8080`` port of the container to ``49160``. Now you can call your app using ``curl`` (install if needed via: @@ -229,7 +229,7 @@ Now you can call your app using ``curl`` (install if needed via: > Hello World 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 +CentOS on Docker. You can get the full source code at https://github.com/gasi/docker-node-hello. Continue to :ref:`running_redis_service`. diff --git a/docs/sources/examples/postgresql_service.rst b/docs/sources/examples/postgresql_service.rst index 35bcc01b9..364977548 100644 --- a/docs/sources/examples/postgresql_service.rst +++ b/docs/sources/examples/postgresql_service.rst @@ -13,7 +13,7 @@ PostgreSQL Service .. note:: - As of version 0.5.2, docker requires root privileges to run. + As of version 0.5.2, Docker requires root privileges to run. You have to either manually adjust your system configuration (permissions on /var/run/docker.sock or sudo config), or prefix `docker` with `sudo`. Check `this thread`_ for details. @@ -24,8 +24,7 @@ PostgreSQL Service Installing PostgreSQL on Docker ------------------------------- -For clarity I won't be showing commands output. - +For clarity I won't be showing command output. Run an interactive shell in Docker container. @@ -62,7 +61,7 @@ Finally, install PostgreSQL 9.2 Now, create a PostgreSQL superuser role that can create databases and other roles. Following Vagrant's convention the role will be named -`docker` with `docker` password assigned to it. +``docker`` with ``docker`` password assigned to it. .. code-block:: bash @@ -108,7 +107,7 @@ Bash prompt; you can also locate it using ``docker ps -a``. .. code-block:: bash - docker commit /postgresql + sudo docker commit /postgresql Finally, run PostgreSQL server via ``docker``. diff --git a/docs/sources/examples/python_web_app.rst b/docs/sources/examples/python_web_app.rst index 8654ab5f1..c707fcdec 100644 --- a/docs/sources/examples/python_web_app.rst +++ b/docs/sources/examples/python_web_app.rst @@ -10,9 +10,9 @@ Python Web App .. include:: example_header.inc The goal of this example is to show you how you can author your own -docker images using a parent image, making changes to it, and then +Docker images using a parent image, making changes to it, and then saving the results as a new image. We will do that by making a simple -hello flask web application image. +hello Flask web application image. **Steps:** @@ -20,22 +20,22 @@ hello flask web application image. sudo docker pull shykes/pybuilder -We are downloading the "shykes/pybuilder" docker image +We are downloading the ``shykes/pybuilder`` Docker image .. code-block:: bash URL=http://github.com/shykes/helloflask/archive/master.tar.gz -We set a URL variable that points to a tarball of a simple helloflask web app +We set a ``URL`` variable that points to a tarball of a simple helloflask web app .. code-block:: bash BUILD_JOB=$(sudo docker run -d -t shykes/pybuilder:latest /usr/local/bin/buildapp $URL) -Inside of the "shykes/pybuilder" image there is a command called -buildapp, we are running that command and passing the $URL variable +Inside of the ``shykes/pybuilder`` image there is a command called +``buildapp``, we are running that command and passing the ``$URL`` variable from step 2 to it, and running the whole thing inside of a new -container. BUILD_JOB will be set with the new container_id. +container. The ``BUILD_JOB`` environment variable will be set with the new container ID. .. code-block:: bash @@ -43,13 +43,13 @@ container. BUILD_JOB will be set with the new container_id. [...] While this container is running, we can attach to the new container to -see what is going on. Ctrl-C to disconnect. +see what is going on. You can use Ctrl-C to disconnect. .. code-block:: bash sudo docker ps -a - -List all docker containers. If this container has already finished + +List all Docker containers. If this container has already finished running, it will still be listed here. .. code-block:: bash @@ -57,8 +57,8 @@ running, it will still be listed here. BUILD_IMG=$(sudo docker commit $BUILD_JOB _/builds/github.com/shykes/helloflask/master) Save the changes we just made in the container to a new image called -``_/builds/github.com/hykes/helloflask/master`` and save the image id in -the BUILD_IMG variable name. +``_/builds/github.com/hykes/helloflask/master`` and save the image ID in +the ``BUILD_IMG`` variable name. .. code-block:: bash @@ -72,24 +72,24 @@ the BUILD_IMG variable name. - **/usr/local/bin/runapp** is the command which starts the web app. Use the new image we just created and create a new container with -network port 5000, and return the container id and store in the -WEB_WORKER variable. +network port 5000, and return the container ID and store in the +``WEB_WORKER`` variable. .. code-block:: bash sudo docker logs $WEB_WORKER * Running on http://0.0.0.0:5000/ -View the logs for the new container using the WEB_WORKER variable, and -if everything worked as planned you should see the line "Running on -http://0.0.0.0:5000/" in the log output. +View the logs for the new container using the ``WEB_WORKER`` variable, and +if everything worked as planned you should see the line ``Running on +http://0.0.0.0:5000/`` in the log output. .. code-block:: bash WEB_PORT=$(sudo docker port $WEB_WORKER 5000) Look up the public-facing port which is NAT-ed. Find the private port -used by the container and store it inside of the WEB_PORT variable. +used by the container and store it inside of the ``WEB_PORT`` variable. .. code-block:: bash @@ -97,8 +97,8 @@ used by the container and store it inside of the WEB_PORT variable. curl http://127.0.0.1:$WEB_PORT Hello world! -Access the web app using curl. If everything worked as planned you -should see the line "Hello world!" inside of your console. +Access the web app using the ``curl`` binary. If everything worked as planned you +should see the line ``Hello world!`` inside of your console. **Video:** diff --git a/docs/sources/examples/running_redis_service.rst b/docs/sources/examples/running_redis_service.rst index 2305980de..ac869501b 100644 --- a/docs/sources/examples/running_redis_service.rst +++ b/docs/sources/examples/running_redis_service.rst @@ -9,7 +9,7 @@ Redis Service .. include:: example_header.inc -Very simple, no frills, redis service. +Very simple, no frills, Redis service. Open a docker container ----------------------- @@ -35,13 +35,13 @@ Snapshot the installation .. code-block:: bash - docker ps -a # grab the container id (this will be the first one in the list) - docker commit /redis + sudo docker ps -a # grab the container id (this will be the first one in the list) + sudo docker commit /redis Run the service --------------- -Running the service with `-d` runs the container in detached mode, leaving the +Running the service with ``-d`` runs the container in detached mode, leaving the container running in the background. Use your snapshot. .. code-block:: bash @@ -51,7 +51,7 @@ container running in the background. Use your snapshot. Test 1 ++++++ -Connect to the container with the redis-cli. +Connect to the container with the ``redis-cli`` binary. .. code-block:: bash @@ -67,7 +67,7 @@ Connect to the container with the redis-cli. Test 2 ++++++ -Connect to the host os with the redis-cli. +Connect to the host os with the ``redis-cli`` binary. .. code-block:: bash diff --git a/docs/sources/examples/running_riak_service.rst b/docs/sources/examples/running_riak_service.rst index d98de6a77..ae08a4b7f 100644 --- a/docs/sources/examples/running_riak_service.rst +++ b/docs/sources/examples/running_riak_service.rst @@ -107,7 +107,7 @@ Create a ``supervisord`` configuration file +++++++++++++++++++++++++++++++++++++++++++ Create an empty file called ``supervisord.conf``. Make sure it's at the same -level as your ``Dockerfile``: +directory level as your ``Dockerfile``: .. code-block:: bash diff --git a/docs/sources/examples/running_ssh_service.rst b/docs/sources/examples/running_ssh_service.rst index db3a71c96..59a80fbf6 100644 --- a/docs/sources/examples/running_ssh_service.rst +++ b/docs/sources/examples/running_ssh_service.rst @@ -12,14 +12,14 @@ SSH Daemon Service **Video:** -I've create a little screencast to show how to create a sshd service +I've create a little screencast to show how to create a SSHd service and connect to it. It is something like 11 minutes and not entirely smooth, but gives you a good idea. .. note:: - This screencast was created before ``docker`` version 0.5.2, so the + This screencast was created before Docker version 0.5.2, so the daemon is unprotected and available via a TCP port. When you run - through the same steps in a newer version of ``docker``, you will + through the same steps in a newer version of Docker, you will need to add ``sudo`` in front of each ``docker`` command in order to reach the daemon over its protected Unix socket. @@ -29,13 +29,14 @@ smooth, but gives you a good idea. -You can also get this sshd container by using -:: +You can also get this sshd container by using: + +.. code-block:: bash sudo docker pull dhrp/sshd -The password is 'screencast' +The password is ``screencast``. **Video's Transcription:** From 4cbb6ce13bf93df2a9e251dfa87fbce9952ab8a6 Mon Sep 17 00:00:00 2001 From: Paul Nasrat Date: Sat, 26 Oct 2013 09:51:56 -0400 Subject: [PATCH 24/71] Introduce failing test case for #2089 --- container_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/container_test.go b/container_test.go index 50942c02d..e863b9d84 100644 --- a/container_test.go +++ b/container_test.go @@ -1652,3 +1652,29 @@ func TestMultipleVolumesFrom(t *testing.T) { t.Fail() } } + +func TestRestartGhost(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + ) + + if err != nil { + t.Fatal(err) + } + if err := container.Kill(); err != nil { + t.Fatal(err) + } + + container.State.Ghost = true + _, err = container.Output() + + if err != nil { + t.Fatal(err) + } +} \ No newline at end of file From a51fb4dd35b2324ff4f1f772202d45a944b9d5e1 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sun, 3 Nov 2013 11:49:29 -0600 Subject: [PATCH 25/71] Added some more colors and animals to increase the pool --- namesgenerator/names-generator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namesgenerator/names-generator.go b/namesgenerator/names-generator.go index afbaf74cf..076bb1cba 100644 --- a/namesgenerator/names-generator.go +++ b/namesgenerator/names-generator.go @@ -11,8 +11,8 @@ type NameChecker interface { } var ( - colors = [...]string{"white", "silver", "gray", "black", "blue", "green", "cyan", "yellow", "gold", "orange", "brown", "red", "violet", "pink", "magenta", "purple"} - animals = [...]string{"ant", "bird", "cat", "chicken", "cow", "dog", "fish", "fox", "horse", "lion", "monkey", "pig", "sheep", "tiger", "whale", "wolf"} + colors = [...]string{"white", "silver", "gray", "black", "blue", "green", "cyan", "yellow", "gold", "orange", "brown", "red", "violet", "pink", "magenta", "purple", "maroon", "crimson", "plum", "fuchsia", "lavender", "slate", "navy", "azure", "aqua", "olive", "teal", "lime", "beige", "tan", "sienna"} + animals = [...]string{"ant", "bear", "bird", "cat", "chicken", "cow", "deer", "dog", "donkey", "duck", "fish", "fox", "frog", "horse", "kangaroo", "koala", "lemur", "lion", "lizard", "monkey", "octopus", "pig", "shark", "sheep", "sloth", "spider", "squirrel", "tiger", "toad", "weasel", "whale", "wolf"} ) func GenerateRandomName(checker NameChecker) (string, error) { From 95708dd35cd94a2c849e37efa05bb4e01ed36bb8 Mon Sep 17 00:00:00 2001 From: Paul Nasrat Date: Sun, 27 Oct 2013 09:04:57 -0400 Subject: [PATCH 26/71] Create new network on dead ghosts on restart. Fixes #2089 --- AUTHORS | 1 + container.go | 23 +++++++++++++++-------- container_test.go | 14 ++++++++------ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/AUTHORS b/AUTHORS index 579ee0c48..4ff695baf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -140,6 +140,7 @@ odk- Pascal Borreli Paul Bowsher Paul Hammond +Paul Nasrat Phil Spitler Piotr Bogdan pysqz diff --git a/container.go b/container.go index 2e52c9532..c330b86e3 100644 --- a/container.go +++ b/container.go @@ -1062,12 +1062,7 @@ func (container *Container) allocateNetwork() error { var iface *NetworkInterface var err error - if !container.State.Ghost { - iface, err = container.runtime.networkManager.Allocate() - if err != nil { - return err - } - } else { + if container.State.Ghost { manager := container.runtime.networkManager if manager.disabled { iface = &NetworkInterface{disabled: true} @@ -1077,8 +1072,20 @@ func (container *Container) allocateNetwork() error { Gateway: manager.bridgeNetwork.IP, manager: manager, } - ipNum := ipToInt(iface.IPNet.IP) - manager.ipAllocator.inUse[ipNum] = struct{}{} + if iface !=nil && iface.IPNet.IP != nil { + ipNum := ipToInt(iface.IPNet.IP) + manager.ipAllocator.inUse[ipNum] = struct{}{} + } else { + iface, err = container.runtime.networkManager.Allocate() + if err != nil { + return err + } + } + } + } else { + iface, err = container.runtime.networkManager.Allocate() + if err != nil { + return err } } diff --git a/container_test.go b/container_test.go index e863b9d84..cbabffc36 100644 --- a/container_test.go +++ b/container_test.go @@ -1657,11 +1657,13 @@ func TestRestartGhost(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := runtime.Create(&Config{ - Image: GetTestImage(runtime).ID, - Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, - Volumes: map[string]struct{}{"/test": {}}, - }, + container, _, err := runtime.Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + "", ) if err != nil { @@ -1677,4 +1679,4 @@ func TestRestartGhost(t *testing.T) { if err != nil { t.Fatal(err) } -} \ No newline at end of file +} From 1f3cdc11ee0c7088c8619f8b31bcd17ce8b79b79 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Wed, 30 Oct 2013 16:26:13 +1000 Subject: [PATCH 27/71] conert docker (commit|import|tag) to use REPOSITORY[:TAG] (and repove support for REPOSITORY TAG) --- commands.go | 21 +++++++++++--------- docs/sources/commandline/cli.rst | 34 +++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/commands.go b/commands.go index 87aa12b65..72927261c 100644 --- a/commands.go +++ b/commands.go @@ -904,7 +904,7 @@ func (cli *DockerCli) CmdKill(args ...string) error { } func (cli *DockerCli) CmdImport(args ...string) error { - cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).") + cmd := Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create a new filesystem image from the contents of a tarball(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).") if err := cmd.Parse(args); err != nil { return nil @@ -913,7 +913,8 @@ func (cli *DockerCli) CmdImport(args ...string) error { cmd.Usage() return nil } - src, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) + src := cmd.Arg(0) + repository, tag := utils.ParseRepositoryTag(cmd.Arg(1)) v := url.Values{} v.Set("repo", repository) v.Set("tag", tag) @@ -1230,14 +1231,16 @@ func (cli *DockerCli) CmdPs(args ...string) error { } func (cli *DockerCli) CmdCommit(args ...string) error { - cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes") + cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes") flComment := cmd.String("m", "", "Commit message") flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith \"") flConfig := cmd.String("run", "", "Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`) if err := cmd.Parse(args); err != nil { return nil } - name, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) + name := cmd.Arg(0) + repository, tag := utils.ParseRepositoryTag(cmd.Arg(1)) + if name == "" { cmd.Usage() return nil @@ -1515,7 +1518,7 @@ func (opts PathOpts) Set(val string) error { } func (cli *DockerCli) CmdTag(args ...string) error { - cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository") + cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY[:TAG]", "Tag an image into a repository") force := cmd.Bool("f", false, "Force") if err := cmd.Parse(args); err != nil { return nil @@ -1526,10 +1529,10 @@ func (cli *DockerCli) CmdTag(args ...string) error { } v := url.Values{} - v.Set("repo", cmd.Arg(1)) - if cmd.NArg() == 3 { - v.Set("tag", cmd.Arg(2)) - } + repository, tag := utils.ParseRepositoryTag(cmd.Arg(1)) + + v.Set("repo", repository) + v.Set("tag", tag) if *force { v.Set("force", "1") diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index c85c60211..26d5f3530 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -133,7 +133,6 @@ to the ``docker`` daemon. ``ADD`` doesn't work when running in this mode because the absence of the context provides no source files to copy to the container. - .. code-block:: bash sudo docker build github.com/creack/docker-firefox @@ -151,7 +150,7 @@ by using the ``git://`` schema. :: - Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY [TAG]] + Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] Create a new image from a container's changes @@ -160,7 +159,26 @@ by using the ``git://`` schema. -run="": Configuration to be applied when the image is launched with `docker run`. (ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}') -Full -run example (multiline is ok within a single quote ``'``) +Simple commit of an existing container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + $ 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 + f5283438590d + $ docker images | head + REPOSITORY TAG ID CREATED SIZE + SvenDowideit/testimage version3 f5283438590d 16 seconds ago 204.2 MB (virtual 335.7 MB) + S + +Full -run example +................. + +(multiline is ok within a single quote ``'``) :: @@ -317,7 +335,7 @@ Displaying images visually :: - Usage: docker import URL|- [REPOSITORY [TAG]] + Usage: docker import URL|- [REPOSITORY[:TAG]] Create a new filesystem image from the contents of a tarball @@ -333,14 +351,16 @@ Examples Import from a remote location ............................. -``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo`` +This will create a new untagged image. + +``$ sudo docker import http://example.com/exampleimage.tgz`` Import from a local file ........................ Import to docker via pipe and standard in -``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal`` +``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new`` Import from a local directory ............................. @@ -716,7 +736,7 @@ The main process inside the container will receive SIGTERM, and after a grace pe :: - Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG] + Usage: docker tag [OPTIONS] IMAGE REPOSITORY[:TAG] Tag an image into a repository From b840b73b08a4f491d0eb8b5a05302342b8bc9242 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 22 Oct 2013 22:22:24 +1000 Subject: [PATCH 28/71] tiny table heading tweak for #2149 - change ID to CONTAINER ID/IMAGE ID or IMAGE (the later if it could be an ID or a repo:tag --- commands.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commands.go b/commands.go index 87aa12b65..91d7a7f4a 100644 --- a/commands.go +++ b/commands.go @@ -820,7 +820,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error { w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintln(w, "ID\tCREATED\tCREATED BY\tSIZE") + fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE") } for _, out := range outs { @@ -1093,7 +1093,7 @@ func (cli *DockerCli) CmdImages(args ...string) error { w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE") + fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE") } for _, out := range outs { @@ -1186,7 +1186,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") + fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") if *size { fmt.Fprintln(w, "\tSIZE") } else { From fbfb7595b141402ecb3efb2a2c9db2a2168bfbe5 Mon Sep 17 00:00:00 2001 From: Oguz Bilgic Date: Mon, 4 Nov 2013 08:20:05 -0500 Subject: [PATCH 29/71] Remove duplicate titles in changelog entries of v0.6.5 --- CHANGELOG.md | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 416c08b03..baa1faac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,40 +4,37 @@ #### Runtime -+ Runtime: Containers can now be named -+ Runtime: Containers can now be linked together for service discovery -+ Runtime: 'run -a', 'start -a' and 'attach' can forward signals to the container for better integration with process supervisors -+ Runtime: Automatically start crashed containers after a reboot -+ Runtime: Expose IP, port, and proto as separate environment vars for container links -* Runtime: Allow ports to be published to specific ips -* Runtime: Prohibit inter-container communication by default -- Runtime: Ignore ErrClosedPipe for stdin in Container.Attach -- Runtime: Fix untag during removal of images -- Runtime: Remove unused field kernelVersion -* Runtime: Fix issue when mounting subdirectories of /mnt in container -* Runtime: Check return value of syscall.Chdir when changing working directory inside dockerinit - -#### Documentation - -* Documentation: Fix the flags for nc in example ++ Containers can now be named ++ Containers can now be linked together for service discovery ++ 'run -a', 'start -a' and 'attach' can forward signals to the container for better integration with process supervisors ++ Automatically start crashed containers after a reboot ++ Expose IP, port, and proto as separate environment vars for container links +* Allow ports to be published to specific ips +* Prohibit inter-container communication by default +- Ignore ErrClosedPipe for stdin in Container.Attach +- Remove unused field kernelVersion +* Fix issue when mounting subdirectories of /mnt in container +- Fix untag during removal of images +* Check return value of syscall.Chdir when changing working directory inside dockerinit #### Client -- Client: Only pass stdin to hijack when needed to avoid closed pipe errors -* Client: Use less reflection in command-line method invocation -- Client: Monitor the tty size after starting the container, not prior -- Client: Remove useless os.Exit() calls after log.Fatal +- Only pass stdin to hijack when needed to avoid closed pipe errors +* Use less reflection in command-line method invocation +- Monitor the tty size after starting the container, not prior +- Remove useless os.Exit() calls after log.Fatal #### Hack -- Hack: Update install.sh with $sh_c to get sudo/su for modprobe -* Hack: Update all the mkimage scripts to use --numeric-owner as a tar argument -* Hack: Update hack/release.sh process to automatically invoke hack/make.sh and bail on build and test issues -+ Hack: Add initial init scripts library and a safer Ubuntu packaging script that works for Debian -* Hack: Add -p option to invoke debootstrap with http_proxy ++ Add initial init scripts library and a safer Ubuntu packaging script that works for Debian +* Add -p option to invoke debootstrap with http_proxy +- Update install.sh with $sh_c to get sudo/su for modprobe +* Update all the mkimage scripts to use --numeric-owner as a tar argument +* Update hack/release.sh process to automatically invoke hack/make.sh and bail on build and test issues #### Other +* Documentation: Fix the flags for nc in example * Testing: Remove warnings and prevent mount issues - Testing: Change logic for tty resize to avoid warning in tests - Builder: Fix race condition in docker build with verbose output From a9ed238bb76774c142107b3f06e8c9e9a1e59e65 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 4 Nov 2013 09:28:40 -0800 Subject: [PATCH 30/71] Ensure container name on register --- runtime.go | 43 ++++++++++++++++++++++++++++++++++++++----- runtime_test.go | 18 +++++++++++++++++- server.go | 5 ++++- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/runtime.go b/runtime.go index 7fb673f43..0ef6786b2 100644 --- a/runtime.go +++ b/runtime.go @@ -113,6 +113,9 @@ func (runtime *Runtime) Register(container *Container) error { if err := validateID(container.ID); err != nil { return err } + if err := runtime.ensureName(container); err != nil { + return err + } // init the wait lock container.waitLock = make(chan struct{}) @@ -174,6 +177,26 @@ func (runtime *Runtime) Register(container *Container) error { return nil } +func (runtime *Runtime) ensureName(container *Container) error { + if container.Name == "" { + name, err := generateRandomName(runtime) + if err != nil { + name = container.ShortID() + } + container.Name = name + + if err := container.ToDisk(); err != nil { + utils.Debugf("Error saving container name %s", err) + } + if !runtime.containerGraph.Exists(name) { + if _, err := runtime.containerGraph.Set(name, container.ID); err != nil { + utils.Debugf("Setting default id - %s", err) + } + } + } + return nil +} + func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error { log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) if err != nil { @@ -518,15 +541,22 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a return img, nil } -func (runtime *Runtime) getFullName(name string) string { +func (runtime *Runtime) getFullName(name string) (string, error) { + if name == "" { + return "", fmt.Errorf("Container name cannot be empty") + } if name[0] != '/' { name = "/" + name } - return name + return name, nil } func (runtime *Runtime) GetByName(name string) (*Container, error) { - entity := runtime.containerGraph.Get(runtime.getFullName(name)) + fullName, err := runtime.getFullName(name) + if err != nil { + return nil, err + } + entity := runtime.containerGraph.Get(fullName) if entity == nil { return nil, fmt.Errorf("Could not find entity for %s", name) } @@ -538,10 +568,13 @@ func (runtime *Runtime) GetByName(name string) (*Container, error) { } func (runtime *Runtime) Children(name string) (map[string]*Container, error) { - name = runtime.getFullName(name) + name, err := runtime.getFullName(name) + if err != nil { + return nil, err + } children := make(map[string]*Container) - err := runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error { + err = runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error { c := runtime.Get(e.ID()) if c == nil { return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p) diff --git a/runtime_test.go b/runtime_test.go index 392e442d3..4e46b7b6d 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -119,7 +119,7 @@ func init() { func setupBaseImage() { config := &DaemonConfig{ - Root: unitTestStoreBase, + Root: unitTestStoreBase, AutoRestart: false, BridgeIface: unitTestNetworkBridge, } @@ -828,3 +828,19 @@ func TestGetAllChildren(t *testing.T) { } } } + +func TestGetFullName(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + name, err := runtime.getFullName("testing") + if err != nil { + t.Fatal(err) + } + if name != "/testing" { + t.Fatalf("Expected /testing got %s", name) + } + if _, err := runtime.getFullName(""); err == nil { + t.Fatal("Error should not be nil") + } +} diff --git a/server.go b/server.go index db185f815..2748c88b1 100644 --- a/server.go +++ b/server.go @@ -1055,7 +1055,10 @@ func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool) if container == nil { return fmt.Errorf("No such link: %s", name) } - name = srv.runtime.getFullName(name) + name, err := srv.runtime.getFullName(name) + if err != nil { + return err + } parent, n := path.Split(name) if parent == "/" { return fmt.Errorf("Conflict, cannot remove the default name of the container") From 957db15ef4cef2f3a6b2933260a7806d69ff68e0 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 4 Nov 2013 10:28:38 -0800 Subject: [PATCH 31/71] Add lock around write operations in graph The graph uses a persistent database connection so a lock is required because our current sqlite3 driver does not implement retry logic when the ErrBusy is received. --- gograph/gograph.go | 17 +++++++++++++++-- gograph/gograph_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/gograph/gograph.go b/gograph/gograph.go index 94ce90afb..32b3a491b 100644 --- a/gograph/gograph.go +++ b/gograph/gograph.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" "path" + "sync" ) const ( @@ -47,6 +48,7 @@ type WalkFunc func(fullPath string, entity *Entity) error // Graph database for storing entities and their relationships type Database struct { conn *sql.DB + mux sync.Mutex } // Create a new graph database initialized with a root entity @@ -54,7 +56,7 @@ func NewDatabase(conn *sql.DB, init bool) (*Database, error) { if conn == nil { return nil, fmt.Errorf("Database connection cannot be nil") } - db := &Database{conn} + db := &Database{conn: conn} if init { if _, err := conn.Exec(createEntityTable); err != nil { @@ -99,7 +101,9 @@ func (db *Database) Close() error { // Set the entity id for a given path func (db *Database) Set(fullPath, id string) (*Entity, error) { - // FIXME: is rollback implicit when closing the connection? + db.mux.Lock() + defer db.mux.Unlock() + rollback := func() { db.conn.Exec("ROLLBACK") } @@ -256,6 +260,9 @@ func (db *Database) RefPaths(id string) Edges { // Delete the reference to an entity at a given path func (db *Database) Delete(name string) error { + db.mux.Lock() + defer db.mux.Unlock() + if name == "/" { return fmt.Errorf("Cannot delete root entity") } @@ -276,6 +283,9 @@ func (db *Database) Delete(name string) error { // Walk the graph to make sure all references to the entity // are removed and return the number of references removed func (db *Database) Purge(id string) (int, error) { + db.mux.Lock() + defer db.mux.Unlock() + rollback := func() { db.conn.Exec("ROLLBACK") } @@ -310,6 +320,9 @@ func (db *Database) Purge(id string) (int, error) { // Rename an edge for a given path func (db *Database) Rename(currentName, newName string) error { + db.mux.Lock() + defer db.mux.Unlock() + parentPath, name := splitPath(currentName) newParentPath, newEdgeName := splitPath(newName) diff --git a/gograph/gograph_test.go b/gograph/gograph_test.go index dba8ed108..1a40fffce 100644 --- a/gograph/gograph_test.go +++ b/gograph/gograph_test.go @@ -3,6 +3,7 @@ package gograph import ( _ "code.google.com/p/gosqlite/sqlite3" "database/sql" + "fmt" "os" "path" "strconv" @@ -501,3 +502,39 @@ func TestGetNameWithTrailingSlash(t *testing.T) { t.Fatalf("Entity should not be nil") } } + +func TestConcurrentWrites(t *testing.T) { + db, dbpath := newTestDb(t) + defer destroyTestDb(dbpath) + + errs := make(chan error, 2) + + save := func(name string, id string) { + if _, err := db.Set(fmt.Sprintf("/%s", name), id); err != nil { + errs <- err + } + errs <- nil + } + purge := func(id string) { + if _, err := db.Purge(id); err != nil { + errs <- err + } + errs <- nil + } + + save("/1", "1") + + go purge("1") + go save("/2", "2") + + any := false + for i := 0; i < 2; i++ { + if err := <-errs; err != nil { + any = true + t.Log(err) + } + } + if any { + t.Fatal() + } +} From 1f44fd8624d1de3f7e1e55880352faef2e141fb1 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 31 Oct 2013 17:58:15 -0700 Subject: [PATCH 32/71] Fix logs with tty --- commands.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/commands.go b/commands.go index 87aa12b65..f7895557d 100644 --- a/commands.go +++ b/commands.go @@ -1347,8 +1347,18 @@ func (cli *DockerCli) CmdLogs(args ...string) error { return nil } name := cmd.Arg(0) + body, _, err := cli.call("GET", "/containers/"+name+"/json", nil) + if err != nil { + return err + } - if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err, nil); err != nil { + container := &Container{} + err = json.Unmarshal(body, container) + if err != nil { + return err + } + + if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", container.Config.Tty, nil, cli.out, cli.err, nil); err != nil { return err } return nil From 177a2f5946623a4d4f90f5a227e57211eed6ade3 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 4 Nov 2013 12:23:35 -0800 Subject: [PATCH 33/71] add CmdLogs test --- commands_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/commands_test.go b/commands_test.go index 67eae04b1..2c0e31978 100644 --- a/commands_test.go +++ b/commands_test.go @@ -645,3 +645,19 @@ func TestRunAutoRemove(t *testing.T) { t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID) } } + +func TestCmdLogs(t *testing.T) { + cli := NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr) + defer cleanup(globalRuntime) + + if err := cli.CmdRun(unitTestImageID, "sh", "-c", "ls -l"); err != nil { + t.Fatal(err) + } + if err := cli.CmdRun("-t", unitTestImageID, "sh", "-c", "ls -l"); err != nil { + t.Fatal(err) + } + + if err := cli.CmdLogs(globalRuntime.List()[0].ID); err != nil { + t.Fatal(err) + } +} From 3061e9f37434fa6bb0714861119ae01d8c4b35fb Mon Sep 17 00:00:00 2001 From: Roberto Gandolfo Hashioka Date: Mon, 4 Nov 2013 13:16:51 -0800 Subject: [PATCH 34/71] - Removed some unconfirmed info - Added reference to the index.rst --- docs/sources/use/index.rst | 1 + docs/sources/use/working_with_volumes.rst | 14 -------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/docs/sources/use/index.rst b/docs/sources/use/index.rst index 7d0b3c5a3..914d5a2a2 100644 --- a/docs/sources/use/index.rst +++ b/docs/sources/use/index.rst @@ -19,3 +19,4 @@ Contents: port_redirection puppet host_integration + working_with_volumes diff --git a/docs/sources/use/working_with_volumes.rst b/docs/sources/use/working_with_volumes.rst index a0e1fbf9e..25febad75 100644 --- a/docs/sources/use/working_with_volumes.rst +++ b/docs/sources/use/working_with_volumes.rst @@ -71,17 +71,3 @@ For example:: The command above mounts the host directory ``/var/logs`` into the container with read only permissions as ``/var/host_logs``. .. versionadded:: v0.5.0 - -Volumes & persistent data storage -================================= - -Current status -.............. - -Docker has volumes which can be reused from container to container. These volumes have the following limitations: - -* they can't be imported and exported (without piping data into and out of the container) -* they can't be backed up and restored from backups (without piping data into and out of the container) -* they can't be stored on custom storage (e.g.: use high IOPS storage for a volume and regular IOPS storage for others) -* it's not possible to cherry pick the volumes to be used from an old container in a new container -* it's not possible to manage the volumes after deleting the containers to which they were attached \ No newline at end of file From 99141ea3ca3c0429ecdfc3bdb097a68957beab71 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 4 Nov 2013 13:20:14 -0800 Subject: [PATCH 35/71] Use parent image config in buildfile --- buildfile.go | 3 +++ buildfile_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/buildfile.go b/buildfile.go index a6322a82a..dbcec5188 100644 --- a/buildfile.go +++ b/buildfile.go @@ -65,6 +65,9 @@ func (b *buildFile) CmdFrom(name string) error { } b.image = image.ID b.config = &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, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") } diff --git a/buildfile_test.go b/buildfile_test.go index c504b81aa..a27ef33cf 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -542,3 +542,39 @@ func TestBuildADDFileNotFound(t *testing.T) { t.Fail() } } + +func TestBuildInheritance(t *testing.T) { + runtime, err := newTestRuntime("") + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{ + runtime: runtime, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), + } + + img := buildImage(testContextTemplate{` + from {IMAGE} + expose 4243 + `, + nil, nil}, t, srv, true) + + img2 := buildImage(testContextTemplate{fmt.Sprintf(` + from %s + entrypoint ["/bin/echo"] + `, img.ID), + nil, nil}, t, srv, true) + + // from child + if img2.Config.Entrypoint[0] != "/bin/echo" { + t.Fail() + } + + // from parent + if img.Config.PortSpecs[0] != "4243" { + t.Fail() + } +} From 854cc5f55992b8bb200f470b11896a0b3cb68d4e Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 4 Nov 2013 15:34:51 -0800 Subject: [PATCH 36/71] remove column size hack --- commands.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/commands.go b/commands.go index 51733035c..e5291ec94 100644 --- a/commands.go +++ b/commands.go @@ -1429,20 +1429,14 @@ func (cli *DockerCli) CmdSearch(args ...string) error { } w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0) fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n") - _, width := cli.getTtySize() - if width == 0 { - width = 45 - } else { - width = width - 10 - 54 //remote the first column - } for _, out := range outs { if (*trusted && !out.IsTrusted) || (*stars > out.StarCount) { continue } desc := strings.Replace(out.Description, "\n", " ", -1) desc = strings.Replace(desc, "\r", " ", -1) - if !*noTrunc && len(desc) > width { - desc = utils.Trunc(desc, width-3) + "..." + if !*noTrunc && len(desc) > 45 { + desc = utils.Trunc(desc, 42) + "..." } fmt.Fprintf(w, "%s\t%s\t%d\t", out.Name, desc, out.StarCount) if out.IsOfficial { From 1182797c6d6bf4bb87e5edcddd75edb3f995dac0 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Mon, 4 Nov 2013 19:18:37 -0600 Subject: [PATCH 37/71] Updated Redis service example to use links --- .../examples/running_redis_service.rst | 111 ++++++++++-------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/docs/sources/examples/running_redis_service.rst b/docs/sources/examples/running_redis_service.rst index ac869501b..edcf73e65 100644 --- a/docs/sources/examples/running_redis_service.rst +++ b/docs/sources/examples/running_redis_service.rst @@ -9,74 +9,93 @@ Redis Service .. include:: example_header.inc -Very simple, no frills, Redis service. +Very simple, no frills, Redis service attached to a web application using a link. -Open a docker container ------------------------ +Create a docker container for Redis +----------------------------------- + +Firstly, we create a ``Dockerfile`` for our new Redis image. .. code-block:: bash - sudo docker run -i -t ubuntu /bin/bash + FROM ubuntu:12.10 + RUN apt-get update + RUN apt-get -y install redis-server + EXPOSE 6379 + ENTRYPOINT ["/usr/bin/redis-server"] -Building your image -------------------- - -Update your Docker container, install the Redis server. Once -installed, exit out of the Docker container. +Next we build an image from our ``Dockerfile``. Replace ```` +with your own user name. .. code-block:: bash - apt-get update - apt-get install redis-server - exit - -Snapshot the installation -------------------------- - -.. code-block:: bash - - sudo docker ps -a # grab the container id (this will be the first one in the list) - sudo docker commit /redis + sudo docker build -t /redis Run the service --------------- +Use the image we've just created and name your container ``redis``. + Running the service with ``-d`` runs the container in detached mode, leaving the -container running in the background. Use your snapshot. +container running in the background. + +Importantly, we're not exposing any ports on our container. Instead we're going to +use a container link to provide access to our Redis database. .. code-block:: bash - sudo docker run -d -p 6379 /redis /usr/bin/redis-server + sudo docker run -name redis -d /redis -Test 1 -++++++ +Create your web application container +------------------------------------- -Connect to the container with the ``redis-cli`` binary. +Next we can create a container for our application. We're going to use the ``-link`` +flag to create a link to the ``redis`` container we've just created with an alias of +``db``. This will create a secure tunnel to the ``redis`` container and expose the +Redis instance running inside that container to only this container. .. code-block:: bash - sudo docker ps # grab the new container id - sudo docker inspect # grab the ipaddress of the container - redis-cli -h -p 6379 - redis 10.0.3.32:6379> set docker awesome + sudo docker run -link redis:db -i -t ubuntu:12.10 /bin/bash + +Once inside our freshly created container we need to install Redis to get the +``redis-cli`` binary to test our connection. + +.. code-block:: bash + + apt-get update + apt-get -y install redis-server + service redis-server stop + +Now we can test the connection. Firstly, let's look at the available environmental +variables in our web application container. We can use these to get the IP and port +of our ``redis`` container. + +.. code-block:: bash + + env + . . . + DB_NAME=/violet_wolf/db + DB_PORT_6379_TCP_PORT=6379 + DB_PORT=tcp://172.17.0.33:6379 + DB_PORT_6379_TCP=tcp://172.17.0.33:6379 + DB_PORT_6379_TCP_ADDR=172.17.0.33 + DB_PORT_6379_TCP_PROTO=tcp + +We can see that we've got a small list of environmental varaibles prefixed with ``DB``. +The ``DB`` comes from the link alias specified when we launched the container. Let's use +the ``DB_PORT_6379_TCP_ADDR`` variable to connect to our Redis container. + +.. code-block:: bash + + redis-cli -h $DB_PORT_6379_TCP_ADDR + redis 172.17.0.33:6379> + redis 172.17.0.33:6379> set docker awesome OK - redis 10.0.3.32:6379> get docker + redis 172.17.0.33:6379> get docker "awesome" - redis 10.0.3.32:6379> exit + redis 172.17.0.33:6379> exit -Test 2 -++++++ +We could easily use this or other environment variables in our web application to make a +connection to our ``redis`` container. -Connect to the host os with the ``redis-cli`` binary. - -.. code-block:: bash - - sudo docker ps # grab the new container id - sudo docker port 6379 # grab the external port - ip addr show # grab the host ip address - redis-cli -h -p - redis 192.168.0.1:49153> set docker awesome - OK - redis 192.168.0.1:49153> get docker - "awesome" - redis 192.168.0.1:49153> exit From fcc0ac61093d0cd0b49b11bb5f7f7501e070bd63 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 4 Nov 2013 21:12:18 -0700 Subject: [PATCH 38/71] Bump Dockerfile to go1.2rc3 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index eefee0344..9bfa266a2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ run apt-get install -y -q mercurial run apt-get install -y -q build-essential libsqlite3-dev # Install Go -run curl -s https://go.googlecode.com/files/go1.2rc2.src.tar.gz | tar -v -C /usr/local -xz +run curl -s https://go.googlecode.com/files/go1.2rc3.src.tar.gz | tar -v -C /usr/local -xz env PATH /usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std From 2cac81393715e6a748b3a2316d5163604497b26c Mon Sep 17 00:00:00 2001 From: pattichen Date: Mon, 4 Nov 2013 22:36:57 -0800 Subject: [PATCH 39/71] Fix typo --- docs/sources/installation/vagrant.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/installation/vagrant.rst b/docs/sources/installation/vagrant.rst index 94efcb0d4..81acf76f5 100644 --- a/docs/sources/installation/vagrant.rst +++ b/docs/sources/installation/vagrant.rst @@ -9,7 +9,7 @@ Using Vagrant (Mac, Linux) This guide will setup a new virtualbox virtual machine with docker installed on your computer. This works on most operating systems, -including MacOX, Windows, Linux, FreeBSD and others. If you can +including MacOSX, Windows, Linux, FreeBSD and others. If you can install these and have at least 400MB RAM to spare you should be good. Install Vagrant and Virtualbox From ff8a4ba0aa20dc3b382e2dcddcd89bb0bb168496 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 5 Nov 2013 07:33:07 -0800 Subject: [PATCH 40/71] Check the output of iptables command. --- iptables/iptables.go | 43 +++++++++++++++++++++++++++++---------- iptables/iptables_test.go | 4 ++-- links.go | 8 ++++++-- network.go | 8 ++++++-- 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/iptables/iptables.go b/iptables/iptables.go index 463f443fd..8139b9881 100644 --- a/iptables/iptables.go +++ b/iptables/iptables.go @@ -27,8 +27,10 @@ type Chain struct { } func NewChain(name, bridge string) (*Chain, error) { - if err := Raw("-t", "nat", "-N", name); err != nil { + if output, err := Raw("-t", "nat", "-N", name); err != nil { return nil, err + } else if len(output) != 0 { + return nil, fmt.Errorf("Error creating new iptables chain: %s", output) } chain := &Chain{ Name: name, @@ -52,13 +54,18 @@ func RemoveExistingChain(name string) error { } func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error { - return Raw("-t", "nat", fmt.Sprint(action), c.Name, + if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name, "-p", proto, "-d", ip.String(), "--dport", strconv.Itoa(port), "!", "-i", c.Bridge, "-j", "DNAT", - "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port))) + "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port))); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error iptables forward: %s", output) + } + return nil } func (c *Chain) Prerouting(action Action, args ...string) error { @@ -66,7 +73,12 @@ func (c *Chain) Prerouting(action Action, args ...string) error { if len(args) > 0 { a = append(a, args...) } - return Raw(append(a, "-j", c.Name)...) + if output, err := Raw(append(a, "-j", c.Name)...); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error iptables prerouting: %s", output) + } + return nil } func (c *Chain) Output(action Action, args ...string) error { @@ -74,7 +86,12 @@ func (c *Chain) Output(action Action, args ...string) error { if len(args) > 0 { a = append(a, args...) } - return Raw(append(a, "-j", c.Name)...) + if output, err := Raw(append(a, "-j", c.Name)...); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error iptables output: %s", output) + } + return nil } func (c *Chain) Remove() error { @@ -94,17 +111,21 @@ func (c *Chain) Remove() error { // Check if an existing rule exists func Exists(args ...string) bool { - return Raw(append([]string{"-C"}, args...)...) == nil + if _, err := Raw(append([]string{"-C"}, args...)...); err != nil { + return false + } + return true } -func Raw(args ...string) error { +func Raw(args ...string) ([]byte, error) { path, err := exec.LookPath("iptables") if err != nil { - return ErrIptablesNotFound + return nil, ErrIptablesNotFound } - if err := exec.Command(path, args...).Run(); err != nil { - return fmt.Errorf("iptables failed: iptables %v", strings.Join(args, " ")) + output, err := exec.Command(path, args...).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err) } - return nil + return output, err } diff --git a/iptables/iptables_test.go b/iptables/iptables_test.go index aad8acdb8..886a63c03 100644 --- a/iptables/iptables_test.go +++ b/iptables/iptables_test.go @@ -6,13 +6,13 @@ import ( ) func TestIptables(t *testing.T) { - if err := Raw("-L"); err != nil { + if _, err := Raw("-L"); err != nil { t.Fatal(err) } path := os.Getenv("PATH") os.Setenv("PATH", "") defer os.Setenv("PATH", path) - if err := Raw("-L"); err == nil { + if _, err := Raw("-L"); err == nil { t.Fatal("Not finding iptables in the PATH should cause an error") } } diff --git a/links.go b/links.go index 60c738abb..3c689d0bf 100644 --- a/links.go +++ b/links.go @@ -120,7 +120,7 @@ func (l *Link) Disable() { func (l *Link) toggle(action string, ignoreErrors bool) error { for _, p := range l.Ports { - if err := iptables.Raw(action, "FORWARD", + if output, err := iptables.Raw(action, "FORWARD", "-i", l.BridgeInterface, "-o", l.BridgeInterface, "-p", p.Proto(), "-s", l.ParentIP, @@ -128,9 +128,11 @@ func (l *Link) toggle(action string, ignoreErrors bool) error { "-d", l.ChildIP, "-j", "ACCEPT"); !ignoreErrors && err != nil { return err + } else if len(output) != 0 { + return fmt.Errorf("Error toggle iptables forward: %s", output) } - if err := iptables.Raw(action, "FORWARD", + if output, err := iptables.Raw(action, "FORWARD", "-i", l.BridgeInterface, "-o", l.BridgeInterface, "-p", p.Proto(), "-s", l.ChildIP, @@ -138,6 +140,8 @@ func (l *Link) toggle(action string, ignoreErrors bool) error { "-d", l.ParentIP, "-j", "ACCEPT"); !ignoreErrors && err != nil { return err + } else if len(output) != 0 { + return fmt.Errorf("Error toggle iptables forward: %s", output) } } return nil diff --git a/network.go b/network.go index c237ccc86..acb94ce1f 100644 --- a/network.go +++ b/network.go @@ -141,9 +141,11 @@ func CreateBridgeIface(config *DaemonConfig) error { } if config.EnableIptables { - if err := iptables.Raw("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr, + if output, err := iptables.Raw("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr, "!", "-d", ifaceAddr, "-j", "MASQUERADE"); 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) } } return nil @@ -656,8 +658,10 @@ func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) { if !config.InterContainerCommunication { if !iptables.Exists(args...) { utils.Debugf("Disable inter-container communication") - if err := iptables.Raw(append([]string{"-A"}, args...)...); err != nil { + if output, err := iptables.Raw(append([]string{"-A"}, args...)...); err != nil { return nil, fmt.Errorf("Unable to prevent intercontainer communication: %s", err) + } else if len(output) != 0 { + return nil, fmt.Errorf("Error enabling iptables: %s", output) } } } else { From 4d5ecdbeb4e428d92c5a0089378f7bae9b3ca3d9 Mon Sep 17 00:00:00 2001 From: Tehmasp Chaudhri Date: Tue, 5 Nov 2013 12:08:56 -0700 Subject: [PATCH 41/71] (doc) Update ubuntulinux.rst small grammar changes - hopefully not worse than original :) --- docs/sources/installation/ubuntulinux.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/installation/ubuntulinux.rst b/docs/sources/installation/ubuntulinux.rst index fd869fbe9..c9cbbe2fd 100644 --- a/docs/sources/installation/ubuntulinux.rst +++ b/docs/sources/installation/ubuntulinux.rst @@ -162,8 +162,8 @@ Verify it worked Docker and UFW ^^^^^^^^^^^^^^ -Docker uses a bridge to manage containers networking, by default UFW -drop all `forwarding`, a first step is to enable forwarding: +Docker uses a bridge to manage container networking. By default, UFW +drops all `forwarding`, thus a first step is to enable UFW forwarding: .. code-block:: bash From e88368b34cab8d4b7d5a1179cdd25fc9cd2b3d79 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Tue, 5 Nov 2013 14:28:57 -0600 Subject: [PATCH 42/71] Removed redundant linking example --- docs/sources/examples/index.rst | 1 - docs/sources/examples/linking_into_redis.rst | 137 ------------------- 2 files changed, 138 deletions(-) delete mode 100644 docs/sources/examples/linking_into_redis.rst diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst index 6167e3565..ea3294d2d 100644 --- a/docs/sources/examples/index.rst +++ b/docs/sources/examples/index.rst @@ -24,4 +24,3 @@ to more substantial services like those which you might find in production. postgresql_service mongodb running_riak_service - linking_into_redis diff --git a/docs/sources/examples/linking_into_redis.rst b/docs/sources/examples/linking_into_redis.rst deleted file mode 100644 index 1b91b8451..000000000 --- a/docs/sources/examples/linking_into_redis.rst +++ /dev/null @@ -1,137 +0,0 @@ -:title: Linking to an Redis container -:description: Running redis linked into your web app -:keywords: docker, example, networking, redis, link - -.. _linking_redis: - -Linking Redis -============= - -.. include:: example_header.inc - -Building a Redis container to link as a child of our web application. - -Building the Redis container ----------------------------- - -Lets build a Redis image with the following Dockerfile. - -First checkout the Redis source code. - -.. code-block:: bash - - git clone https://github.com/antirez/redis.git - cd redis - git checkout 2.6 - - -Now let's create a Dockerfile in the root of the Redis repository. - -.. code-block:: bash - - # Build redis from source - # Make sure you have the redis source code checked out in - # the same directory as this Dockerfile - FROM ubuntu - - RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list - RUN apt-get update - RUN apt-get upgrade -y - - RUN apt-get install -y gcc make g++ build-essential libc6-dev tcl - - ADD . /redis - - RUN (cd /redis && make) - - RUN mkdir -p /redis-data - VOLUME ["/redis-data"] - EXPOSE 6379 - - ENTRYPOINT ["/redis/src/redis-server"] - CMD ["--dir", "/redis-data"] - - # docker build our new redis image from source - docker build -t redis-2.6 . - - -We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports -to connect to our Redis container on. If you do not expose any ports for the -image then docker will not be able to establish the link between containers. - - -Run the Redis container ------------------------ - -.. code-block:: bash - - sudo docker run -d -e PASSWORD=docker -name redis redis-2.6 --requirepass docker - -This will run our Redis container with the password docker -to secure our service. By specifying the ``-name`` flag on run -we will assign the name ``redis`` to this container. If we do not specify a name for -our container via the ``-name`` flag docker will automatically generate a name for us. -We can issue all the commands that you would expect; start, stop, attach, using the name for our container. -The name also allows us to link other containers into this one. - -Linking Redis as a child ------------------------- - -Next we can start a new web application that has a dependency on Redis and apply a link -to connect both containers. If you noticed when running our Redis server we did not use -the ``-p`` flag to publish the Redis port to the host system. Redis exposed port 6379 via the Dockerfile -and this is all we need to establish a link. - -Now let's start our web application with a link into Redis. - -.. code-block:: bash - - sudo docker run -t -i -link redis:db -name webapp ubuntu bash - - root@4c01db0b339c:/# env - - HOSTNAME=4c01db0b339c - DB_NAME=/webapp/db - TERM=xterm - DB_PORT=tcp://172.17.0.8:6379 - DB_PORT_6379_TCP=tcp://172.17.0.8:6379 - DB_PORT_6379_TCP_PROTO=tcp - DB_PORT_6379_TCP_ADDR=172.17.0.8 - DB_PORT_6379_TCP_PORT=6379 - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - PWD=/ - DB_ENV_PASSWORD=docker - SHLVL=1 - HOME=/ - container=lxc - _=/usr/bin/env - root@4c01db0b339c:/# - - -When we inspect the environment of the linked container we can see a few extra environment -variables have been added. When you specified ``-link redis:db`` you are telling docker -to link the container named ``redis`` into this new container with the alias ``db``. -Environment variables are prefixed with the alias so that the parent container can access -network and environment information from the containers that are linked into it. - -.. code-block:: bash - - # The name of the child container - DB_NAME=/webapp/db - - # The default protocol, ip, and port of the service running in the container - DB_PORT=tcp://172.17.0.8:6379 - - # A specific protocol, ip, and port of various services - DB_PORT_6379_TCP=tcp://172.17.0.8:6379 - DB_PORT_6379_TCP_PROTO=tcp - DB_PORT_6379_TCP_ADDR=172.17.0.8 - DB_PORT_6379_TCP_PORT=6379 - - # Get environment variables of the container - DB_ENV_PASSWORD=docker - - -Accessing the network information along with the environment of the child container allows -us to easily connect to the Redis service on the specific IP and port and use the password -specified in the environment. From 4b8c41c4a2591797f97ad079e4314b21498ba732 Mon Sep 17 00:00:00 2001 From: unclejack Date: Sat, 19 Oct 2013 01:44:06 +0300 Subject: [PATCH 43/71] disallow / as source for bind mount in the cli This makes the docker cli reject docker run commands which include bind mounts like "/:/some/path/in/the/container". Bind mounting the root directory is a bad idea and the cli should throw an error right away. The same check will also be made by the remote API via another commit. --- container.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/container.go b/container.go index a22484c2d..92e05283d 100644 --- a/container.go +++ b/container.go @@ -251,6 +251,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, for bind := range flVolumes { arr := strings.Split(bind, ":") if len(arr) > 1 { + if arr[0] == "/" { + return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'") + } dstDir := arr[1] flVolumes[dstDir] = struct{}{} binds = append(binds, bind) From f1f39616eb052455a383de589bbc1eaa9ab5d96c Mon Sep 17 00:00:00 2001 From: unclejack Date: Sat, 19 Oct 2013 01:53:15 +0300 Subject: [PATCH 44/71] add test to ensure / can't be bind mounted This adds a test which checks that we're erroring out when we attempt to bind mount root in a container. --- commands_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/commands_test.go b/commands_test.go index 2c0e31978..e33ba9c7c 100644 --- a/commands_test.go +++ b/commands_test.go @@ -660,4 +660,22 @@ func TestCmdLogs(t *testing.T) { if err := cli.CmdLogs(globalRuntime.List()[0].ID); err != nil { t.Fatal(err) } + +// Expected behaviour: using / as a bind mount source should throw an error +func TestRunErrorBindMountRootSource(t *testing.T) { + + cli := NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr) + defer cleanup(globalRuntime) + + c := make(chan struct{}) + go func() { + defer close(c) + if err := cli.CmdRun("-v", "/:/tmp", unitTestImageID, "echo 'should fail'"); err == nil { + t.Fatal("should have failed to run when using / as a source for the bind mount") + } + }() + + setTimeout(t, "CmdRun timed out", 5*time.Second, func() { + <-c + }) } From 4d2ba779e1a0596c51cc6ed2ddb7c2139830f15c Mon Sep 17 00:00:00 2001 From: unclejack Date: Sat, 19 Oct 2013 01:56:52 +0300 Subject: [PATCH 45/71] validate bind mounts on the server side This changes the server side code to make sure that: 1) the source of a bind mount isn't / The bind mount "/:/foo" isn't allowed. 2) Check that the source exists The source to be bind mounted must exist. This fixes issue #2070. --- server.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/server.go b/server.go index 314df0256..cbe7c6435 100644 --- a/server.go +++ b/server.go @@ -1316,6 +1316,25 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error { func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error { runtime := srv.runtime container := runtime.Get(name) + + if hostConfig != nil { + for _, bind := range hostConfig.Binds { + splitBind := strings.Split(bind, ":") + source := splitBind[0] + + // refuse to bind mount "/" to the container + if source == "/" { + return fmt.Errorf("Invalid bind mount '%s' : source can't be '/'", bind) + } + + // ensure the source exists on the host + _, err := os.Stat(source) + if err != nil && os.IsNotExist(err) { + return fmt.Errorf("Invalid bind mount '%s' : source doesn't exist", bind) + } + } + } + if container == nil { return fmt.Errorf("No such container: %s", name) } From 35430e89201972d877ba01405f966882a1d3888a Mon Sep 17 00:00:00 2001 From: unclejack Date: Sat, 19 Oct 2013 02:06:25 +0300 Subject: [PATCH 46/71] test: error out when bind mount source doesn't exist This adds a test to verify that the server is checking whether the path to be bind mounted actually exists on the server. --- commands_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/commands_test.go b/commands_test.go index e33ba9c7c..b38ed3313 100644 --- a/commands_test.go +++ b/commands_test.go @@ -679,3 +679,22 @@ func TestRunErrorBindMountRootSource(t *testing.T) { <-c }) } + +// Expected behaviour: error out when attempting to bind mount non-existing source paths +func TestRunErrorBindNonExistingSource(t *testing.T) { + + cli := NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr) + defer cleanup(globalRuntime) + + c := make(chan struct{}) + go func() { + defer close(c) + if err := cli.CmdRun("-v", "/i/dont/exist:/tmp", unitTestImageID, "echo 'should fail'"); err == nil { + t.Fatal("should have failed to run when using /i/dont/exist as a source for the bind mount") + } + }() + + setTimeout(t, "CmdRun timed out", 5*time.Second, func() { + <-c + }) +} From 5af87d147580555c72e6a1d448ab663f991cc737 Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Tue, 5 Nov 2013 13:21:25 -0800 Subject: [PATCH 47/71] contrib: Desktop integration. Firefox usecase. --- contrib/desktop-integration/README.txt | 11 +++++ contrib/desktop-integration/data/Dockerfile | 38 ++++++++++++++ .../desktop-integration/firefox/Dockerfile | 49 +++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 contrib/desktop-integration/README.txt create mode 100644 contrib/desktop-integration/data/Dockerfile create mode 100644 contrib/desktop-integration/firefox/Dockerfile diff --git a/contrib/desktop-integration/README.txt b/contrib/desktop-integration/README.txt new file mode 100644 index 000000000..2f55c979e --- /dev/null +++ b/contrib/desktop-integration/README.txt @@ -0,0 +1,11 @@ +Desktop Integration +=================== + +The ./contrib/desktop-integration contains examples of typical dockerized +desktop applications. + +Examples +======== + +* Data container: ./data/Dockerfile creates a data image sharing /data volume +* Firefox: ./firefox/Dockerfile shows a way to dockerize a common multimedia application diff --git a/contrib/desktop-integration/data/Dockerfile b/contrib/desktop-integration/data/Dockerfile new file mode 100644 index 000000000..453afdd3d --- /dev/null +++ b/contrib/desktop-integration/data/Dockerfile @@ -0,0 +1,38 @@ +# 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.github.com/dotcloud/docker/master/contrib/desktop-integration/data/Dockerfile +# +# # Build data image +# docker build -t data -rm . +# +# # Create a data container. (eg: firefox-data) +# docker run -name firefox-data data true +# +# # List data from it +# docker run -volumes-from firefox-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/firefox/Dockerfile b/contrib/desktop-integration/firefox/Dockerfile new file mode 100644 index 000000000..f8924f4b4 --- /dev/null +++ b/contrib/desktop-integration/firefox/Dockerfile @@ -0,0 +1,49 @@ +# VERSION: 0.7 +# DESCRIPTION: Create firefox container with its dependencies +# AUTHOR: Daniel Mizyrycki +# COMMENTS: +# This file describes how to build a Firefox container with all +# dependencies installed. It uses native X11 unix socket and alsa +# sound devices. Tested on Debian 7.2 +# USAGE: +# # Download Firefox Dockerfile +# wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/firefox/Dockerfile +# +# # Build firefox image +# docker build -t firefox -rm . +# +# # Run stateful data-on-host firefox. For ephemeral, remove -v /data/firefox:/data +# docker run -v /data/firefox:/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 firefox +# +# # To run stateful dockerized data containers +# docker run -volumes-from firefox-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 firefox + +docker-version 0.6.5 + +# Base docker image +from tianon/debian:wheezy +maintainer Daniel Mizyrycki + +# Install firefox dependencies +run echo "deb http://ftp.debian.org/debian/ wheezy main contrib" > /etc/apt/sources.list +run apt-get update +run DEBIAN_FRONTEND=noninteractive apt-get install -y libXrender1 libasound2 \ + libdbus-glib-1-2 libgtk2.0-0 libpango1.0-0 libxt6 wget bzip2 sudo + +# Install Firefox +run mkdir /application +run cd /application; wget -O - \ + http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/25.0/linux-x86_64/en-US/firefox-25.0.tar.bz2 | tar jx + +# 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 firefox. -no-remote is necessary to create a new container, as firefox +# appears to communicate with itself through X11. +cmd ["/bin/sh", "-c", "/usr/bin/sudo -u sysadmin -H -E /application/firefox/firefox -no-remote"] From cdf0cf495d0e0f0bbfdd8021f9cd6e68a8234dd4 Mon Sep 17 00:00:00 2001 From: unclejack Date: Wed, 6 Nov 2013 01:41:54 +0200 Subject: [PATCH 48/71] fix build for commands_test --- commands_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/commands_test.go b/commands_test.go index b38ed3313..1ec005bdd 100644 --- a/commands_test.go +++ b/commands_test.go @@ -660,6 +660,7 @@ func TestCmdLogs(t *testing.T) { if err := cli.CmdLogs(globalRuntime.List()[0].ID); err != nil { t.Fatal(err) } +} // Expected behaviour: using / as a bind mount source should throw an error func TestRunErrorBindMountRootSource(t *testing.T) { From 3560c922b193e0f6b06c715a7b920f90a5990859 Mon Sep 17 00:00:00 2001 From: Mark Allen Date: Sat, 17 Aug 2013 22:49:33 -0500 Subject: [PATCH 49/71] Prevent DNS server conflicts in CreateBridgeIface Retrieve /etc/resolv.conf data (if available) Add checkNameserverOverlaps and call it to make sure there are no conflicts Add utils.GetNameserversAsCIDR and tests Read /etc/resolv.conf and pull out nameservers, formatting them as a CIDR block ("1.2.3.4/32") --- network.go | 31 +++++++++++++++++++++++++++++-- network_test.go | 16 ++++++++++++++++ utils/utils.go | 18 ++++++++++++++++++ utils/utils_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 2 deletions(-) diff --git a/network.go b/network.go index c237ccc86..efd0736a3 100644 --- a/network.go +++ b/network.go @@ -76,6 +76,21 @@ func checkRouteOverlaps(networks []*net.IPNet, dockerNetwork *net.IPNet) error { return nil } +func checkNameserverOverlaps(nameservers []string, dockerNetwork *net.IPNet) error { + if len(nameservers) > 0 { + for _, ns := range nameservers { + _, nsNetwork, err := net.ParseCIDR(ns) + if err != nil { + return err + } + if networkOverlaps(dockerNetwork, nsNetwork) { + return fmt.Errorf("%s overlaps nameserver %s", dockerNetwork, nsNetwork) + } + } + } + return nil +} + // CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, // and attempts to configure it with an address which doesn't conflict with any other interface on the host. // If it can't find an address which doesn't conflict, it will return an error. @@ -100,6 +115,16 @@ func CreateBridgeIface(config *DaemonConfig) error { "192.168.44.1/24", } + nameservers := []string{} + resolvConf, _ := utils.GetResolvConf() + // we don't check for an error here, because we don't really care + // if we can't read /etc/resolv.conf. So instead we skip the append + // if resolvConf is nil. It either doesn't exist, or we can't read it + // for some reason. + if resolvConf != nil { + nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...) + } + var ifaceAddr string for _, addr := range addrs { _, dockerNetwork, err := net.ParseCIDR(addr) @@ -111,8 +136,10 @@ func CreateBridgeIface(config *DaemonConfig) error { return err } if err := checkRouteOverlaps(routes, dockerNetwork); err == nil { - ifaceAddr = addr - break + if err := checkNameserverOverlaps(nameservers, dockerNetwork); err == nil { + ifaceAddr = addr + break + } } else { utils.Debugf("%s: %s", addr, err) } diff --git a/network_test.go b/network_test.go index 7304e205a..e2631ddcb 100644 --- a/network_test.go +++ b/network_test.go @@ -295,3 +295,19 @@ func TestCheckRouteOverlaps(t *testing.T) { t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't") } } + +func TestCheckNameserverOverlaps(t *testing.T) { + nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"} + + _, netX, _ := net.ParseCIDR("10.0.2.3/32") + + if err := checkNameserverOverlaps(nameservers, netX); err == nil { + t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX) + } + + _, netX, _ = net.ParseCIDR("192.168.102.2/32") + + if err := checkNameserverOverlaps(nameservers, netX); err != nil { + t.Fatalf("%s should not overlap %v but it does", netX, nameservers) + } +} diff --git a/utils/utils.go b/utils/utils.go index cadd09503..4941eae42 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -16,6 +16,7 @@ import ( "os/exec" "path/filepath" "runtime" + "regexp" "strconv" "strings" "sync" @@ -903,6 +904,23 @@ func StripComments(input []byte, commentMarker []byte) []byte { return output } +// GetNameserversAsCIDR returns nameservers (if any) listed in +// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") +// This function's output is intended for net.ParseCIDR +func GetNameserversAsCIDR(resolvConf []byte) []string { + var parsedResolvConf = StripComments(resolvConf, []byte("#")) + nameservers := []string{} + re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]\.){3}([0-9]))\s*$`) + for _, line := range bytes.Split(parsedResolvConf, []byte("\n")) { + var ns = re.FindSubmatch(line) + if len(ns) > 0 { + nameservers = append(nameservers, string(ns[1])+"/32") + } + } + + return nameservers +} + func ParseHost(host string, port int, addr string) (string, error) { var proto string switch { diff --git a/utils/utils_test.go b/utils/utils_test.go index 49f19bf75..f10bb40d3 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -444,3 +444,41 @@ func TestParsePortMapping(t *testing.T) { t.Fail() } } + +func TestGetNameserversAsCIDR(t *testing.T) { + for resolv, result := range map[string][]string{` +nameserver 1.2.3.4 +nameserver 4.3.2.1 +search example.com`: {"1.2.3.4/32", "4.3.2.1/32"}, + `search example.com`: {}, + `nameserver 1.2.3.4 +search example.com +nameserver 4.3.2.1`: []string{"1.2.3.4/32", "4.3.2.1/32"}, + ``: []string{}, + ` nameserver 1.2.3.4 `: []string{"1.2.3.4/32"}, + `search example.com +nameserver 1.2.3.4 +#nameserver 4.3.2.1`: []string{"1.2.3.4/32"}, + `search example.com +nameserver 1.2.3.4 # not 4.3.2.1`: []string{"1.2.3.4/32"}, + } { + test := GetNameserversAsCIDR([]byte(resolv)) + if !StrSlicesEqual(test, result) { + t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + +func StrSlicesEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + + return true +} From 15867ff4308c5c1330a400be56410d404336100f Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Sun, 6 Oct 2013 05:44:04 +0000 Subject: [PATCH 50/71] rework images JSON --- api_params.go | 6 +++--- commands.go | 34 ++++++++++++++++++---------------- server.go | 39 ++++++++++++++++++++++++++++----------- sorter.go | 2 +- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/api_params.go b/api_params.go index f666ac239..d796b9654 100644 --- a/api_params.go +++ b/api_params.go @@ -9,12 +9,12 @@ type APIHistory struct { } type APIImages struct { - Repository string `json:",omitempty"` - Tag string `json:",omitempty"` - ID string `json:"Id"` + ID string `json:"Id"` + RepoTags []string `json:",omitempty"` Created int64 Size int64 VirtualSize int64 + ParentId string `json:",omitempty"` } type APIInfo struct { diff --git a/commands.go b/commands.go index d52114553..7fc3dceb4 100644 --- a/commands.go +++ b/commands.go @@ -1097,27 +1097,29 @@ func (cli *DockerCli) CmdImages(args ...string) error { fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE") } + var repo string + var tag string for _, out := range outs { - if out.Repository == "" { - out.Repository = "" - } - if out.Tag == "" { - out.Tag = "" - } + for _, repotag := range out.RepoTags { - if !*noTrunc { - out.ID = utils.TruncateID(out.ID) - } + components := strings.SplitN(repotag, ":", 2) + repo = components[0] + tag = components[1] - if !*quiet { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t", out.Repository, out.Tag, out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) - if out.VirtualSize > 0 { - fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize)) + if !*noTrunc { + out.ID = utils.TruncateID(out.ID) + } + + if !*quiet { + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t", repo, tag, out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) + if out.VirtualSize > 0 { + fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.Size), utils.HumanSize(out.VirtualSize)) + } else { + fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size)) + } } else { - fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size)) + fmt.Fprintln(w, out.ID) } - } else { - fmt.Fprintln(w, out.ID) } } diff --git a/server.go b/server.go index cbe7c6435..2492c8868 100644 --- a/server.go +++ b/server.go @@ -284,7 +284,7 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { if err != nil { return nil, err } - outs := []APIImages{} //produce [] when empty instead of 'null' + lookup := make(map[string]APIImages) for name, repository := range srv.runtime.repositories.Repositories { if filter != "" { if match, _ := path.Match(filter, name); !match { @@ -292,23 +292,40 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { } } for tag, id := range repository { - var out APIImages image, err := srv.runtime.graph.Get(id) if err != nil { log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err) continue } - delete(allImages, id) - out.Repository = name - out.Tag = tag - out.ID = image.ID - out.Created = image.Created.Unix() - out.Size = image.Size - out.VirtualSize = image.getParentsSize(0) + image.Size - outs = append(outs, out) + + if out, exists := lookup[id]; exists { + out.RepoTags = append(out.RepoTags, fmt.Sprintf("%s:%s", name, tag)) + + lookup[id] = out + } else { + var out APIImages + + delete(allImages, id) + + out.ParentId = image.Parent + out.RepoTags = []string{fmt.Sprintf("%s:%s", name, tag)} + out.ID = image.ID + out.Created = image.Created.Unix() + out.Size = image.Size + out.VirtualSize = image.getParentsSize(0) + image.Size + + lookup[id] = out + } + } } - // Display images which aren't part of a + + outs := make([]APIImages, 0, len(lookup)) + for _, value := range lookup { + outs = append(outs, value) + } + + // Display images which aren't part of a repository/tag if filter == "" { for _, image := range allImages { var out APIImages diff --git a/sorter.go b/sorter.go index d4331eaf1..c9a86b45c 100644 --- a/sorter.go +++ b/sorter.go @@ -25,7 +25,7 @@ func (s *imageSorter) Less(i, j int) bool { // Sort []ApiImages by most recent creation date and tag name. func sortImagesByCreationAndTag(images []APIImages) { creationAndTag := func(i1, i2 *APIImages) bool { - return i1.Created > i2.Created || (i1.Created == i2.Created && i2.Tag > i1.Tag) + return i1.Created > i2.Created } sorter := &imageSorter{ From d7928b9a673e9b2aff36fa6961481312455c91f4 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Sun, 6 Oct 2013 05:59:49 +0000 Subject: [PATCH 51/71] bump api version and ensure backward compat --- api.go | 13 +++++++++++-- api_params.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/api.go b/api.go index 0c06b2e6d..00673de33 100644 --- a/api.go +++ b/api.go @@ -23,7 +23,7 @@ import ( ) const ( - APIVERSION = 1.6 + APIVERSION = 1.7 DEFAULTHTTPHOST = "127.0.0.1" DEFAULTHTTPPORT = 4243 DEFAULTUNIXSOCKET = "/var/run/docker.sock" @@ -191,7 +191,16 @@ func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http. return err } - return writeJSON(w, http.StatusOK, outs) + if version < 1.7 { + outs2 := []APIImagesOld{} + for _, ctnr := range outs { + outs2 = append(outs2, ctnr.ToLegacy()...) + } + + return writeJSON(w, http.StatusOK, outs2) + } else { + return writeJSON(w, http.StatusOK, outs) + } } func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { diff --git a/api_params.go b/api_params.go index d796b9654..e4508542e 100644 --- a/api_params.go +++ b/api_params.go @@ -1,5 +1,7 @@ package docker +import "strings" + type APIHistory struct { ID string `json:"Id"` Tags []string `json:",omitempty"` @@ -17,6 +19,35 @@ type APIImages struct { ParentId string `json:",omitempty"` } +type APIImagesOld struct { + Repository string `json:",omitempty"` + Tag string `json:",omitempty"` + ID string `json:"Id"` + Created int64 + Size int64 + VirtualSize int64 +} + +func (self *APIImages) ToLegacy() []APIImagesOld { + + outs := []APIImagesOld{} + for _, repotag := range self.RepoTags { + + components := strings.SplitN(repotag, ":", 2) + + outs = append(outs, APIImagesOld{ + ID: self.ID, + Repository: components[0], + Tag: components[1], + Created: self.Created, + Size: self.Size, + VirtualSize: self.VirtualSize, + }) + } + + return outs +} + type APIInfo struct { Debug bool Containers int From dcf0279a50e7a2eca97b205725df474a516b8f79 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Sun, 6 Oct 2013 16:36:38 +0000 Subject: [PATCH 52/71] fix "images -a" --- server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server.go b/server.go index 2492c8868..ab8193f4c 100644 --- a/server.go +++ b/server.go @@ -330,6 +330,7 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { for _, image := range allImages { var out APIImages out.ID = image.ID + out.RepoTags = []string{":"} out.Created = image.Created.Unix() out.Size = image.Size out.VirtualSize = image.getParentsSize(0) + image.Size From 2a5998baf172f94a8d6866e46207a0d7d75e70e5 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Tue, 8 Oct 2013 13:41:44 +0000 Subject: [PATCH 53/71] move "images -viz" to client --- api.go | 8 -------- commands.go | 24 ++++++++++++++++++++++-- server.go | 39 +-------------------------------------- 3 files changed, 23 insertions(+), 48 deletions(-) diff --git a/api.go b/api.go index 00673de33..095095954 100644 --- a/api.go +++ b/api.go @@ -203,13 +203,6 @@ func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http. } } -func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if err := srv.ImagesViz(w); err != nil { - return err - } - return nil -} - func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { return writeJSON(w, http.StatusOK, srv.DockerInfo()) } @@ -1046,7 +1039,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { "/info": getInfo, "/version": getVersion, "/images/json": getImagesJSON, - "/images/viz": getImagesViz, "/images/search": getImagesSearch, "/images/{name:.*}/history": getImagesHistory, "/images/{name:.*}/json": getImagesByName, diff --git a/commands.go b/commands.go index 7fc3dceb4..b3cd7958a 100644 --- a/commands.go +++ b/commands.go @@ -1067,11 +1067,31 @@ func (cli *DockerCli) CmdImages(args ...string) error { } if *flViz { - body, _, err := cli.call("GET", "/images/viz", false) + body, _, err := cli.call("GET", "/images/json?all=1", nil) if err != nil { return err } - fmt.Fprintf(cli.out, "%s", body) + + var outs []APIImages + err = json.Unmarshal(body, &outs) + if err != nil { + return err + } + + fmt.Fprintf(cli.out, "digraph docker {\n") + + for _, image := range outs { + if image.ParentId == "" { + fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", utils.TruncateID(image.ID)) + } else { + fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", utils.TruncateID(image.ParentId), utils.TruncateID(image.ID)) + } + if image.RepoTags[0] != ":" { + fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n", utils.TruncateID(image.ID), utils.TruncateID(image.ID), strings.Join(image.RepoTags, "\\n")) + } + } + + fmt.Fprintf(cli.out, " base [style=invisible]\n}\n") } else { v := url.Values{} if cmd.NArg() == 1 { diff --git a/server.go b/server.go index ab8193f4c..644a433ee 100644 --- a/server.go +++ b/server.go @@ -233,44 +233,6 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils. return img.ShortID(), nil } -func (srv *Server) ImagesViz(out io.Writer) error { - images, _ := srv.runtime.graph.Map() - if images == nil { - return nil - } - out.Write([]byte("digraph docker {\n")) - - var ( - parentImage *Image - err error - ) - for _, image := range images { - parentImage, err = image.GetParent() - if err != nil { - return err - } - if parentImage != nil { - out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n")) - } else { - out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n")) - } - } - - reporefs := make(map[string][]string) - - for name, repository := range srv.runtime.repositories.Repositories { - for tag, id := range repository { - reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag)) - } - } - - for id, repos := range reporefs { - out.Write([]byte(" \"" + id + "\" [label=\"" + id + "\\n" + strings.Join(repos, "\\n") + "\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n")) - } - out.Write([]byte(" base [style=invisible]\n}\n")) - return nil -} - func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { var ( allImages map[string]*Image @@ -330,6 +292,7 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { for _, image := range allImages { var out APIImages out.ID = image.ID + out.ParentId = image.Parent out.RepoTags = []string{":"} out.Created = image.Created.Unix() out.Size = image.Size From 8f64759881b3d48e42927ed9bdf1c9162d3c8bb3 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Tue, 8 Oct 2013 13:52:36 +0000 Subject: [PATCH 54/71] bring back /images/viz, but 404 for latest --- api.go | 13 +++++++++++++ server.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/api.go b/api.go index 095095954..61252ab9a 100644 --- a/api.go +++ b/api.go @@ -203,6 +203,18 @@ func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http. } } +func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if version > 1.6 { + w.WriteHeader(http.StatusNotFound) + return fmt.Errorf("This is now implemented in the client.") + } + + if err := srv.ImagesViz(w); err != nil { + return err + } + return nil +} + func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { return writeJSON(w, http.StatusOK, srv.DockerInfo()) } @@ -1039,6 +1051,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { "/info": getInfo, "/version": getVersion, "/images/json": getImagesJSON, + "/images/viz": getImagesViz, "/images/search": getImagesSearch, "/images/{name:.*}/history": getImagesHistory, "/images/{name:.*}/json": getImagesByName, diff --git a/server.go b/server.go index 644a433ee..344102902 100644 --- a/server.go +++ b/server.go @@ -233,6 +233,44 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils. return img.ShortID(), nil } +func (srv *Server) ImagesViz(out io.Writer) error { + images, _ := srv.runtime.graph.Map() + if images == nil { + return nil + } + out.Write([]byte("digraph docker {\n")) + + var ( + parentImage *Image + err error + ) + for _, image := range images { + parentImage, err = image.GetParent() + if err != nil { + return fmt.Errorf("Error while getting parent image: %v", err) + } + if parentImage != nil { + out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n")) + } else { + out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n")) + } + } + + reporefs := make(map[string][]string) + + for name, repository := range srv.runtime.repositories.Repositories { + for tag, id := range repository { + reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag)) + } + } + + for id, repos := range reporefs { + out.Write([]byte(" \"" + id + "\" [label=\"" + id + "\\n" + strings.Join(repos, "\\n") + "\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n")) + } + out.Write([]byte(" base [style=invisible]\n}\n")) + return nil +} + func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { var ( allImages map[string]*Image From ec9a9a08b8b76079207ced5b55a7caade27fb3d3 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Wed, 9 Oct 2013 14:38:39 +0000 Subject: [PATCH 55/71] implement "images -tree" --- commands.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/commands.go b/commands.go index b3cd7958a..c793b27c0 100644 --- a/commands.go +++ b/commands.go @@ -1057,6 +1057,7 @@ func (cli *DockerCli) CmdImages(args ...string) error { all := cmd.Bool("a", false, "show all images") noTrunc := cmd.Bool("notrunc", false, "Don't truncate output") flViz := cmd.Bool("viz", false, "output graph in graphviz format") + flTree := cmd.Bool("tree", false, "output graph in tree format") if err := cmd.Parse(args); err != nil { return nil @@ -1092,6 +1093,52 @@ func (cli *DockerCli) CmdImages(args ...string) error { } fmt.Fprintf(cli.out, " base [style=invisible]\n}\n") + } else if *flTree { + body, _, err := cli.call("GET", "/images/json?all=1", nil) + if err != nil { + return err + } + + var outs []APIImages + err = json.Unmarshal(body, &outs) + if err != nil { + return err + } + + var startImageArg = cmd.Arg(0) + var startImage APIImages + + var roots []APIImages + var byParent = make(map[string][]APIImages) + for _, image := range outs { + if image.ParentId == "" { + roots = append(roots, image) + } else { + if children, exists := byParent[image.ParentId]; exists { + byParent[image.ParentId] = append(children, image) + } else { + byParent[image.ParentId] = []APIImages{image} + } + } + + if startImageArg != "" { + if startImageArg == image.ID || startImageArg == utils.TruncateID(image.ID) { + startImage = image + } + + for _, repotag := range image.RepoTags { + if repotag == startImageArg { + startImage = image + } + } + } + } + + if startImageArg != "" { + WalkTree(cli, noTrunc, []APIImages{startImage}, byParent, "") + } else { + WalkTree(cli, noTrunc, roots, byParent, "") + } } else { v := url.Values{} if cmd.NArg() == 1 { @@ -1150,6 +1197,47 @@ func (cli *DockerCli) CmdImages(args ...string) error { return nil } +func WalkTree(cli *DockerCli, noTrunc *bool, images []APIImages, byParent map[string][]APIImages, prefix string) { + if len(images) > 1 { + length := len(images) + for index, image := range images { + if index+1 == length { + PrintTreeNode(cli, noTrunc, image, prefix+"└─") + if subimages, exists := byParent[image.ID]; exists { + WalkTree(cli, noTrunc, subimages, byParent, prefix+" ") + } + } else { + PrintTreeNode(cli, noTrunc, image, prefix+"|─") + if subimages, exists := byParent[image.ID]; exists { + WalkTree(cli, noTrunc, subimages, byParent, prefix+"| ") + } + } + } + } else { + for _, image := range images { + PrintTreeNode(cli, noTrunc, image, prefix+"└─") + if subimages, exists := byParent[image.ID]; exists { + WalkTree(cli, noTrunc, subimages, byParent, prefix+" ") + } + } + } +} + +func PrintTreeNode(cli *DockerCli, noTrunc *bool, image APIImages, prefix string) { + var imageID string + if *noTrunc { + imageID = image.ID + } else { + imageID = utils.TruncateID(image.ID) + } + + if image.RepoTags[0] != ":" { + fmt.Fprintf(cli.out, "%s%s Tags: %s\n", prefix, imageID, strings.Join(image.RepoTags, ",")) + } else { + fmt.Fprintf(cli.out, "%s%s\n", prefix, imageID) + } +} + func displayablePorts(ports []APIPort) string { result := []string{} for _, port := range ports { From 059bb7a262a7620d8c7c5fdd064af352ae242f65 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Sun, 13 Oct 2013 13:42:01 +0000 Subject: [PATCH 56/71] make existing tests pass --- api_test.go | 31 +++---------------------------- server_test.go | 18 +++++++++--------- sorter_test.go | 32 +++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 44 deletions(-) diff --git a/api_test.go b/api_test.go index fcca8ce83..002736200 100644 --- a/api_test.go +++ b/api_test.go @@ -184,7 +184,7 @@ func TestGetImagesJSON(t *testing.T) { found := false for _, img := range images { - if img.Repository == unitTestImageName { + if strings.Contains(img.RepoTags[0], unitTestImageName) { found = true break } @@ -275,31 +275,6 @@ func TestGetImagesJSON(t *testing.T) { } } -func TestGetImagesViz(t *testing.T) { - runtime := mkRuntime(t) - defer nuke(runtime) - - srv := &Server{runtime: runtime} - - r := httptest.NewRecorder() - if err := getImagesViz(srv, APIVERSION, r, nil, nil); err != nil { - t.Fatal(err) - } - - if r.Code != http.StatusOK { - t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code) - } - - reader := bufio.NewReader(r.Body) - line, err := reader.ReadString('\n') - if err != nil { - t.Fatal(err) - } - if line != "digraph docker {\n" { - t.Errorf("Expected digraph docker {\n, %s found", line) - } -} - func TestGetImagesHistory(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) @@ -1226,7 +1201,7 @@ func TestDeleteImages(t *testing.T) { t.Fatal(err) } - if len(images) != len(initialImages)+1 { + if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+1 { t.Errorf("Expected %d images, %d found", len(initialImages)+1, len(images)) } @@ -1265,7 +1240,7 @@ func TestDeleteImages(t *testing.T) { t.Fatal(err) } - if len(images) != len(initialImages) { + if len(images[0].RepoTags) != len(initialImages[0].RepoTags) { t.Errorf("Expected %d image, %d found", len(initialImages), len(images)) } diff --git a/server_test.go b/server_test.go index 7f9cbaadf..4072344f3 100644 --- a/server_test.go +++ b/server_test.go @@ -34,8 +34,8 @@ func TestContainerTagImageDelete(t *testing.T) { t.Fatal(err) } - if len(images) != len(initialImages)+3 { - t.Errorf("Expected %d images, %d found", len(initialImages)+2, len(images)) + if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+3 { + t.Errorf("Expected %d images, %d found", len(initialImages)+3, len(images)) } if _, err := srv.ImageDelete("utest/docker:tag2", true); err != nil { @@ -47,7 +47,7 @@ func TestContainerTagImageDelete(t *testing.T) { t.Fatal(err) } - if len(images) != len(initialImages)+2 { + if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+2 { t.Errorf("Expected %d images, %d found", len(initialImages)+2, len(images)) } @@ -60,7 +60,7 @@ func TestContainerTagImageDelete(t *testing.T) { t.Fatal(err) } - if len(images) != len(initialImages)+1 { + if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+1 { t.Errorf("Expected %d images, %d found", len(initialImages)+1, len(images)) } @@ -462,7 +462,7 @@ func TestRmi(t *testing.T) { if strings.Contains(unitTestImageID, image.ID) { continue } - if image.Repository == "" { + if image.RepoTags[0] == ":" { t.Fatalf("Expected tagged image, got untagged one.") } } @@ -490,7 +490,7 @@ func TestImagesFilter(t *testing.T) { t.Fatal(err) } - if len(images) != 2 { + if len(images[0].RepoTags) != 2 { t.Fatal("incorrect number of matches returned") } @@ -499,7 +499,7 @@ func TestImagesFilter(t *testing.T) { t.Fatal(err) } - if len(images) != 1 { + if len(images[0].RepoTags) != 1 { t.Fatal("incorrect number of matches returned") } @@ -508,7 +508,7 @@ func TestImagesFilter(t *testing.T) { t.Fatal(err) } - if len(images) != 1 { + if len(images[0].RepoTags) != 1 { t.Fatal("incorrect number of matches returned") } @@ -517,7 +517,7 @@ func TestImagesFilter(t *testing.T) { t.Fatal(err) } - if len(images) != 1 { + if len(images[0].RepoTags) != 1 { t.Fatal("incorrect number of matches returned") } } diff --git a/sorter_test.go b/sorter_test.go index d61b1a711..54f647132 100644 --- a/sorter_test.go +++ b/sorter_test.go @@ -3,6 +3,7 @@ package docker import ( "fmt" "testing" + "time" ) func TestServerListOrderedImagesByCreationDate(t *testing.T) { @@ -34,29 +35,46 @@ func TestServerListOrderedImagesByCreationDateAndTag(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - archive, err := fakeTar() + err := generateImage("bar", runtime) if err != nil { t.Fatal(err) } - image, err := runtime.graph.Create(archive, nil, "Testing", "", nil) + + time.Sleep(time.Second) + + err = generateImage("zed", runtime) if err != nil { t.Fatal(err) } srv := &Server{runtime: runtime} - srv.ContainerTag(image.ID, "repo", "foo", false) - srv.ContainerTag(image.ID, "repo", "bar", false) - images, err := srv.Images(true, "") if err != nil { t.Fatal(err) } - if images[0].Created != images[1].Created || images[0].Tag >= images[1].Tag { - t.Error("Expected []APIImges to be ordered by most recent creation date and tag name.") + if images[0].RepoTags[0] != "repo:zed" && images[0].RepoTags[0] != "repo:bar" { + t.Errorf("Expected []APIImges to be ordered by most recent creation date. %s", images) } } +func generateImage(name string, runtime *Runtime) error { + + archive, err := fakeTar() + if err != nil { + return err + } + image, err := runtime.graph.Create(archive, nil, "Testing", "", nil) + if err != nil { + return err + } + + srv := &Server{runtime: runtime} + srv.ContainerTag(image.ID, "repo", name, false) + + return nil +} + func TestSortUniquePorts(t *testing.T) { ports := []Port{ Port("6379/tcp"), From d9fe0647cb464ccd6f79ac33fb1d1ac56441f6b0 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Sun, 13 Oct 2013 22:36:05 +0000 Subject: [PATCH 57/71] adding test for "images -viz" --- commands_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ runtime_test.go | 1 + 2 files changed, 49 insertions(+) diff --git a/commands_test.go b/commands_test.go index 1ec005bdd..28a19d2af 100644 --- a/commands_test.go +++ b/commands_test.go @@ -6,6 +6,7 @@ import ( "github.com/dotcloud/docker/utils" "io" "io/ioutil" + "regexp" "strings" "testing" "time" @@ -699,3 +700,50 @@ func TestRunErrorBindNonExistingSource(t *testing.T) { <-c }) } + +func TestImagesViz(t *testing.T) { + stdout, stdoutPipe := io.Pipe() + + cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) + defer cleanup(globalRuntime) + + c := make(chan struct{}) + go func() { + defer close(c) + if err := cli.CmdImages("-viz"); err != nil { + t.Fatal(err) + } + stdoutPipe.Close() + }() + + setTimeout(t, "Reading command output time out", 2*time.Second, func() { + cmdOutputBytes, err := ioutil.ReadAll(bufio.NewReader(stdout)) + if err != nil { + t.Fatal(err) + } + cmdOutput := string(cmdOutputBytes) + + regexpStrings := []string{ + "digraph docker {", + fmt.Sprintf("base -> \"%s\" \\[style=invis]", unitTestImageIDShort), + fmt.Sprintf("label=\"%s\\\\n%s:latest\"", unitTestImageIDShort, unitTestImageName), + "base \\[style=invisible]", + } + + compiledRegexps := []*regexp.Regexp{} + for _, regexpString := range regexpStrings { + regexp, err := regexp.Compile(regexpString) + if err != nil { + fmt.Println("Error in regex string: ", err) + return + } + compiledRegexps = append(compiledRegexps, regexp) + } + + for _, regexp := range compiledRegexps { + if !regexp.MatchString(cmdOutput) { + t.Fatalf("images -viz content '%s' did not match regexp '%s'", cmdOutput, regexp) + } + } + }) +} diff --git a/runtime_test.go b/runtime_test.go index 4e46b7b6d..e61f63d49 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -22,6 +22,7 @@ import ( const ( unitTestImageName = "docker-test-image" unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 + unitTestImageIDShort = "83599e29c455" unitTestNetworkBridge = "testdockbr0" unitTestStoreBase = "/var/lib/docker/unit-tests" testDaemonAddr = "127.0.0.1:4270" From f1aeac361aadc90a35e1b839933afbf9ad27a1a1 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Sun, 27 Oct 2013 23:13:00 +0000 Subject: [PATCH 58/71] adding test for "images -tree" --- commands_test.go | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/commands_test.go b/commands_test.go index 28a19d2af..bed9557bb 100644 --- a/commands_test.go +++ b/commands_test.go @@ -707,6 +707,9 @@ func TestImagesViz(t *testing.T) { cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) defer cleanup(globalRuntime) + srv := &Server{runtime: globalRuntime} + image := buildTestImages(t, srv) + c := make(chan struct{}) go func() { defer close(c) @@ -727,6 +730,7 @@ func TestImagesViz(t *testing.T) { "digraph docker {", fmt.Sprintf("base -> \"%s\" \\[style=invis]", unitTestImageIDShort), fmt.Sprintf("label=\"%s\\\\n%s:latest\"", unitTestImageIDShort, unitTestImageName), + fmt.Sprintf("label=\"%s\\\\n%s:%s\"", utils.TruncateID(image.ID), "test", "latest"), "base \\[style=invisible]", } @@ -747,3 +751,77 @@ func TestImagesViz(t *testing.T) { } }) } + +func TestImagesTree(t *testing.T) { + stdout, stdoutPipe := io.Pipe() + + cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr) + defer cleanup(globalRuntime) + + srv := &Server{runtime: globalRuntime} + image := buildTestImages(t, srv) + + c := make(chan struct{}) + go func() { + defer close(c) + if err := cli.CmdImages("-tree"); err != nil { + t.Fatal(err) + } + stdoutPipe.Close() + }() + + setTimeout(t, "Reading command output time out", 2*time.Second, func() { + cmdOutputBytes, err := ioutil.ReadAll(bufio.NewReader(stdout)) + if err != nil { + t.Fatal(err) + } + cmdOutput := string(cmdOutputBytes) + + regexpStrings := []string{ + fmt.Sprintf("└─%s Tags: %s:latest", unitTestImageIDShort, unitTestImageName), + "(?m)^ └─[0-9a-f]+", + "(?m)^ └─[0-9a-f]+", + "(?m)^ └─[0-9a-f]+", + fmt.Sprintf(" └─%s Tags: test:latest", utils.TruncateID(image.ID)), + } + + compiledRegexps := []*regexp.Regexp{} + for _, regexpString := range regexpStrings { + regexp, err := regexp.Compile(regexpString) + if err != nil { + fmt.Println("Error in regex string: ", err) + return + } + compiledRegexps = append(compiledRegexps, regexp) + } + + for _, regexp := range compiledRegexps { + if !regexp.MatchString(cmdOutput) { + t.Fatalf("images -tree content '%s' did not match regexp '%s'", cmdOutput, regexp) + } + } + }) +} + +func buildTestImages(t *testing.T, srv *Server) *Image { + + var testBuilder = testContextTemplate{ + ` +from {IMAGE} +run sh -c 'echo root:testpass > /tmp/passwd' +run mkdir -p /var/run/sshd +run [ "$(cat /tmp/passwd)" = "root:testpass" ] +run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ] +`, + nil, + nil, + } + image := buildImage(testBuilder, t, srv, true) + + err := srv.ContainerTag(image.ID, "test", "latest", false) + if err != nil { + t.Fatal(err) + } + + return image +} From b7d1d35c278c57ad4732dc23e529cf771158ee96 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Sun, 27 Oct 2013 23:42:09 +0000 Subject: [PATCH 59/71] add image size to -tree --- commands.go | 5 +++-- commands_test.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/commands.go b/commands.go index c793b27c0..8cb51d419 100644 --- a/commands.go +++ b/commands.go @@ -1231,10 +1231,11 @@ func PrintTreeNode(cli *DockerCli, noTrunc *bool, image APIImages, prefix string imageID = utils.TruncateID(image.ID) } + fmt.Fprintf(cli.out, "%s%s Size: %s (virtual %s)", prefix, imageID, utils.HumanSize(image.Size), utils.HumanSize(image.VirtualSize)) if image.RepoTags[0] != ":" { - fmt.Fprintf(cli.out, "%s%s Tags: %s\n", prefix, imageID, strings.Join(image.RepoTags, ",")) + fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.RepoTags, ",")) } else { - fmt.Fprintf(cli.out, "%s%s\n", prefix, imageID) + fmt.Fprint(cli.out, "\n") } } diff --git a/commands_test.go b/commands_test.go index bed9557bb..479e295dd 100644 --- a/commands_test.go +++ b/commands_test.go @@ -778,11 +778,11 @@ func TestImagesTree(t *testing.T) { cmdOutput := string(cmdOutputBytes) regexpStrings := []string{ - fmt.Sprintf("└─%s Tags: %s:latest", unitTestImageIDShort, unitTestImageName), + fmt.Sprintf("└─%s Size: (\\d+.\\d+ MB) \\(virtual \\d+.\\d+ MB\\) Tags: %s:latest", unitTestImageIDShort, unitTestImageName), "(?m)^ └─[0-9a-f]+", "(?m)^ └─[0-9a-f]+", "(?m)^ └─[0-9a-f]+", - fmt.Sprintf(" └─%s Tags: test:latest", utils.TruncateID(image.ID)), + fmt.Sprintf(" └─%s Size: \\d+.\\d+ kB \\(virtual \\d+.\\d+ MB\\) Tags: test:latest", utils.TruncateID(image.ID)), } compiledRegexps := []*regexp.Regexp{} From 6d5b73c2a6d668b11d6b5c643a45170265bf73c0 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Mon, 28 Oct 2013 04:02:24 +0000 Subject: [PATCH 60/71] update "images" documentation --- docs/sources/api/docker_remote_api.rst | 108 +- docs/sources/api/docker_remote_api_v1.7.rst | 1187 +++++++++++++++++++ docs/sources/commandline/cli.rst | 30 + 3 files changed, 1323 insertions(+), 2 deletions(-) create mode 100644 docs/sources/api/docker_remote_api_v1.7.rst diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 86cacd17d..d5cb1a44a 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -26,14 +26,118 @@ Docker Remote API 2. Versions =========== -The current version of the API is 1.6 +The current version of the API is 1.7 Calling /images//insert is the same as calling -/v1.6/images//insert +/v1.7/images//insert You can still call an old version of the api using /v1.0/images//insert +v1.7 +**** + +Full Documentation +------------------ + +:doc:`docker_remote_api_v1.7` + +What's new +---------- + +.. http:get:: /images/json + + The format of the json returned from this uri changed. Instead of an entry + for each repo/tag on an image, each image is only represented once, with a + nested attribute indicating the repo/tags that apply to that image. + + Instead of: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "VirtualSize": 131506275, + "Size": 131506275, + "Created": 1365714795, + "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c", + "Tag": "12.04", + "Repository": "ubuntu" + }, + { + "VirtualSize": 131506275, + "Size": 131506275, + "Created": 1365714795, + "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c", + "Tag": "latest", + "Repository": "ubuntu" + }, + { + "VirtualSize": 131506275, + "Size": 131506275, + "Created": 1365714795, + "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c", + "Tag": "precise", + "Repository": "ubuntu" + }, + { + "VirtualSize": 180116135, + "Size": 24653, + "Created": 1364102658, + "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Tag": "12.10", + "Repository": "ubuntu" + }, + { + "VirtualSize": 180116135, + "Size": 24653, + "Created": 1364102658, + "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Tag": "quantal", + "Repository": "ubuntu" + } + ] + + The returned json looks like this: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "RepoTag": [ + "ubuntu:12.04", + "ubuntu:precise", + "ubuntu:latest" + ], + "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c", + "Created": 1365714795, + "Size": 131506275, + "VirtualSize": 131506275 + }, + { + "RepoTag": [ + "ubuntu:12.10", + "ubuntu:quantal" + ], + "ParentId": "27cf784147099545", + "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Created": 1364102658, + "Size": 24653, + "VirtualSize": 180116135 + } + ] + +.. http:get:: /images/viz + + This URI no longer exists. The ``images -viz`` output is now generated in + the client, using the ``/images/json`` data. + v1.6 **** diff --git a/docs/sources/api/docker_remote_api_v1.7.rst b/docs/sources/api/docker_remote_api_v1.7.rst new file mode 100644 index 000000000..d1b094c87 --- /dev/null +++ b/docs/sources/api/docker_remote_api_v1.7.rst @@ -0,0 +1,1187 @@ +:title: Remote API v1.7 +:description: API Documentation for Docker +:keywords: API, Docker, rcli, REST, documentation + +:orphan: + +====================== +Docker Remote API v1.7 +====================== + +.. contents:: Table of Contents + +1. Brief introduction +===================== + +- The Remote API is replacing rcli +- Default port in the docker daemon is 4243 +- 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 +*************** + +.. http:get:: /containers/json + + List containers + + **Example request**: + + .. sourcecode:: http + + GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + 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 all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default + :query limit: Show ``limit`` last created containers, include non-running ones. + :query since: Show only containers created since Id, include non-running ones. + :query before: Show only containers created before Id, include non-running ones. + :query size: 1/True/true or 0/False/false, Show the containers sizes + :statuscode 200: no error + :statuscode 400: bad parameter + :statuscode 500: server error + + +Create a container +****************** + +.. http:post:: /containers/create + + Create a container + + **Example request**: + + .. sourcecode:: http + + POST /containers/create HTTP/1.1 + Content-Type: application/json + + { + "Hostname":"", + "User":"", + "Memory":0, + "MemorySwap":0, + "AttachStdin":false, + "AttachStdout":true, + "AttachStderr":true, + "PortSpecs":null, + "Privileged": false, + "Tty":false, + "OpenStdin":false, + "StdinOnce":false, + "Env":null, + "Cmd":[ + "date" + ], + "Dns":null, + "Image":"base", + "Volumes":{}, + "VolumesFrom":"", + "WorkingDir":"" + + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 201 OK + Content-Type: application/json + + { + "Id":"e90e34656806" + "Warnings":[] + } + + :jsonparam config: the container's configuration + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 406: impossible to attach (container not running) + :statuscode 500: server error + + +Inspect a container +******************* + +.. http:get:: /containers/(id)/json + + Return low-level information on the container ``id`` + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/json HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + 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/dotcloud/docker/bin/docker", + "ResolvConfPath": "/etc/resolv.conf", + "Volumes": {} + } + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +List processes running inside a container +***************************************** + +.. http:get:: /containers/(id)/top + + List processes running inside the container ``id`` + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/top HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + 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 ps_args: ps arguments to use (eg. aux) + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Inspect changes on a container's filesystem +******************************************* + +.. http:get:: /containers/(id)/changes + + Inspect changes on container ``id`` 's filesystem + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/changes HTTP/1.1 + + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Path":"/dev", + "Kind":0 + }, + { + "Path":"/dev/kmsg", + "Kind":1 + }, + { + "Path":"/test", + "Kind":1 + } + ] + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Export a container +****************** + +.. http:get:: /containers/(id)/export + + Export the contents of container ``id`` + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/export HTTP/1.1 + + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/octet-stream + + {{ STREAM }} + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Start a container +***************** + +.. http:post:: /containers/(id)/start + + Start the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/(id)/start HTTP/1.1 + Content-Type: application/json + + { + "Binds":["/tmp:/tmp"], + "LxcConf":{"lxc.utsname":"docker"} + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 No Content + Content-Type: text/plain + + :jsonparam hostConfig: the container's host configuration (optional) + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Stop a container +**************** + +.. http:post:: /containers/(id)/stop + + Stop the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/e90e34656806/stop?t=5 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :query t: number of seconds to wait before killing the container + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Restart a container +******************* + +.. http:post:: /containers/(id)/restart + + Restart the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/e90e34656806/restart?t=5 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :query t: number of seconds to wait before killing the container + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Kill a container +**************** + +.. http:post:: /containers/(id)/kill + + Kill the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/e90e34656806/kill HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Attach to a container +********************* + +.. http:post:: /containers/(id)/attach + + Attach to the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + {{ STREAM }} + + :query logs: 1/True/true or 0/False/false, return logs. Default false + :query stream: 1/True/true or 0/False/false, return stream. Default false + :query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false + :query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false + :query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false + :statuscode 200: no error + :statuscode 400: bad parameter + :statuscode 404: no such container + :statuscode 500: server error + + **Stream details**: + + When using the TTY setting is enabled in + :http: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 writen on stdout) + - 1: stdout + - 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 +**************** + +.. http:post:: /containers/(id)/wait + + Block until container ``id`` stops, then returns the exit code + + **Example request**: + + .. sourcecode:: http + + POST /containers/16253994b7c4/wait HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"StatusCode":0} + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Remove a container +******************* + +.. http:delete:: /containers/(id) + + Remove the container ``id`` from the filesystem + + **Example request**: + + .. sourcecode:: http + + DELETE /containers/16253994b7c4?v=1 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false + :statuscode 204: no error + :statuscode 400: bad parameter + :statuscode 404: no such container + :statuscode 500: server error + + +Copy files or folders from a container +************************************** + +.. http:post:: /containers/(id)/copy + + Copy files or folders of container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/4fa6e0f0c678/copy HTTP/1.1 + Content-Type: application/json + + { + "Resource":"test.txt" + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/octet-stream + + {{ STREAM }} + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +2.2 Images +---------- + +List Images +*********** + +.. http:get:: /images/(format) + + List images ``format`` could be json or viz (json default) + + **Example request**: + + .. sourcecode:: http + + GET /images/json?all=0 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "RepoTag": [ + "ubuntu:12.04", + "ubuntu:precise", + "ubuntu:latest" + ], + "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c", + "Created": 1365714795, + "Size": 131506275, + "VirtualSize": 131506275 + }, + { + "RepoTag": [ + "ubuntu:12.10", + "ubuntu:quantal" + ], + "ParentId": "27cf784147099545", + "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Created": 1364102658, + "Size": 24653, + "VirtualSize": 180116135 + } + ] + + +Create an image +*************** + +.. http:post:: /images/create + + Create an image, either by pull it from the registry or by importing it + + **Example request**: + + .. sourcecode:: http + + POST /images/create?fromImage=base HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Pulling..."} + {"status":"Pulling", "progress":"1/? (n/a)"} + {"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 fromImage: name of the image to pull + :query fromSrc: source to import, - means stdin + :query repo: repository + :query tag: tag + :query registry: the registry to pull from + :statuscode 200: no error + :statuscode 500: server error + + +Insert a file in an image +************************* + +.. http:post:: /images/(name)/insert + + Insert a file from ``url`` in the image ``name`` at ``path`` + + **Example request**: + + .. sourcecode:: http + + POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Inserting..."} + {"status":"Inserting", "progress":"1/? (n/a)"} + {"error":"Invalid..."} + ... + + :statuscode 200: no error + :statuscode 500: server error + + +Inspect an image +**************** + +.. http:get:: /images/(name)/json + + Return low-level information on the image ``name`` + + **Example request**: + + .. sourcecode:: http + + GET /images/base/json HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "parent":"27cf784147099545", + "created":"2013-03-23T22:24:18.818426-07:00", + "container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0", + "container_config": + { + "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":"" + }, + "Size": 6824592 + } + + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 500: server error + + +Get the history of an image +*************************** + +.. http:get:: /images/(name)/history + + Return the history of the image ``name`` + + **Example request**: + + .. sourcecode:: http + + GET /images/base/history HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Id":"b750fe79269d", + "Created":1364102658, + "CreatedBy":"/bin/bash" + }, + { + "Id":"27cf78414709", + "Created":1364068391, + "CreatedBy":"" + } + ] + + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 500: server error + + +Push an image on the registry +***************************** + +.. http:post:: /images/(name)/push + + Push the image ``name`` on the registry + + **Example request**: + + .. sourcecode:: http + + POST /images/test/push HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Pushing..."} + {"status":"Pushing", "progress":"1/? (n/a)"} + {"error":"Invalid..."} + ... + + The ``X-Registry-Auth`` header can be used to include a + base64-encoded AuthConfig object. + + :query registry: the registry you wan to push, optional + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 500: server error + + +Tag an image into a repository +****************************** + +.. http:post:: /images/(name)/tag + + Tag the image ``name`` into a repository + + **Example request**: + + .. sourcecode:: http + + POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + + :query repo: The repository to tag in + :query force: 1/True/true or 0/False/false, default false + :statuscode 200: no error + :statuscode 400: bad parameter + :statuscode 404: no such image + :statuscode 409: conflict + :statuscode 500: server error + + +Remove an image +*************** + +.. http:delete:: /images/(name) + + Remove the image ``name`` from the filesystem + + **Example request**: + + .. sourcecode:: http + + DELETE /images/test HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-type: application/json + + [ + {"Untagged":"3e2f21a89f"}, + {"Deleted":"3e2f21a89f"}, + {"Deleted":"53b4f83ac9"} + ] + + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 409: conflict + :statuscode 500: server error + + +Search images +************* + +.. http:get:: /images/search + + Search for an image in the docker index + + **Example request**: + + .. sourcecode:: http + + GET /images/search?term=sshd HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Name":"cespare/sshd", + "Description":"" + }, + { + "Name":"johnfuller/sshd", + "Description":"" + }, + { + "Name":"dhrp/mongodb-sshd", + "Description":"" + } + ] + + :query term: term to search + :statuscode 200: no error + :statuscode 500: server error + + +2.3 Misc +-------- + +Build an image from Dockerfile via stdin +**************************************** + +.. http:post:: /build + + Build an image from Dockerfile via stdin + + **Example request**: + + .. sourcecode:: http + + POST /build HTTP/1.1 + + {{ STREAM }} + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + + {{ STREAM }} + + + 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). + + The Content-type header should be set to "application/tar". + + :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success + :query q: suppress verbose build output + :query nocache: do not use the cache when building the image + :statuscode 200: no error + :statuscode 500: server error + + +Check auth configuration +************************ + +.. http:post:: /auth + + Get the default username and email + + **Example request**: + + .. sourcecode:: http + + 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**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + + :statuscode 200: no error + :statuscode 204: no error + :statuscode 500: server error + + +Display system-wide information +******************************* + +.. http:get:: /info + + Display system-wide information + + **Example request**: + + .. sourcecode:: http + + GET /info HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Containers":11, + "Images":16, + "Debug":false, + "NFd": 11, + "NGoroutines":21, + "MemoryLimit":true, + "SwapLimit":false, + "IPv4Forwarding":true + } + + :statuscode 200: no error + :statuscode 500: server error + + +Show the docker version information +*********************************** + +.. http:get:: /version + + Show the docker version information + + **Example request**: + + .. sourcecode:: http + + GET /version HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Version":"0.2.2", + "GitCommit":"5a2a5cc+CHANGES", + "GoVersion":"go1.0.3" + } + + :statuscode 200: no error + :statuscode 500: server error + + +Create a new image from a container's changes +********************************************* + +.. http:post:: /commit + + Create a new image from a container's changes + + **Example request**: + + .. sourcecode:: http + + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 201 OK + Content-Type: application/vnd.docker.raw-stream + + {"Id":"596069db4bf5"} + + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Monitor Docker's events +*********************** + +.. http:get:: /events + + Get events from docker, either in real time via streaming, or via polling (using `since`) + + **Example request**: + + .. sourcecode:: http + + POST /events?since=1374067924 + + **Example response**: + + .. sourcecode:: http + + 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 since: timestamp used for polling + :statuscode 200: no error + :statuscode 500: server error + + +3. Going further +================ + +3.1 Inside 'docker run' +----------------------- + +Here are the steps of 'docker run' : + +* 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 +* 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. + +.. code-block:: bash + + docker -d -H="192.168.1.9:4243" -api-enable-cors + diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 6c2826567..c064a3cef 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -328,6 +328,36 @@ Displaying images visually .. image:: docker_images.gif :alt: Example inheritance graph of Docker images. + +Displaying image hierarchy +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + sudo docker images -tree + + |─8dbd9e392a96 Size: 131.5 MB (virtual 131.5 MB) Tags: ubuntu:12.04,ubuntu:latest,ubuntu:precise + └─27cf78414709 Size: 180.1 MB (virtual 180.1 MB) + └─b750fe79269d Size: 24.65 kB (virtual 180.1 MB) Tags: ubuntu:12.10,ubuntu:quantal + |─f98de3b610d5 Size: 12.29 kB (virtual 180.1 MB) + | └─7da80deb7dbf Size: 16.38 kB (virtual 180.1 MB) + | └─65ed2fee0a34 Size: 20.66 kB (virtual 180.2 MB) + | └─a2b9ea53dddc Size: 819.7 MB (virtual 999.8 MB) + | └─a29b932eaba8 Size: 28.67 kB (virtual 999.9 MB) + | └─e270a44f124d Size: 12.29 kB (virtual 999.9 MB) Tags: progrium/buildstep:latest + └─17e74ac162d8 Size: 53.93 kB (virtual 180.2 MB) + └─339a3f56b760 Size: 24.65 kB (virtual 180.2 MB) + └─904fcc40e34d Size: 96.7 MB (virtual 276.9 MB) + └─b1b0235328dd Size: 363.3 MB (virtual 640.2 MB) + └─7cb05d1acb3b Size: 20.48 kB (virtual 640.2 MB) + └─47bf6f34832d Size: 20.48 kB (virtual 640.2 MB) + └─f165104e82ed Size: 12.29 kB (virtual 640.2 MB) + └─d9cf85a47b7e Size: 1.911 MB (virtual 642.2 MB) + └─3ee562df86ca Size: 17.07 kB (virtual 642.2 MB) + └─b05fc2d00e4a Size: 24.96 kB (virtual 642.2 MB) + └─c96a99614930 Size: 12.29 kB (virtual 642.2 MB) + └─a6a357a48c49 Size: 12.29 kB (virtual 642.2 MB) Tags: ndj/mongodb:latest + .. _cli_import: ``import`` From 22ef38ee79c2f353578f3642918a44126b4cb48c Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Mon, 28 Oct 2013 04:35:24 +0000 Subject: [PATCH 61/71] fixup for rebase --- commands_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands_test.go b/commands_test.go index 479e295dd..6c6a8e975 100644 --- a/commands_test.go +++ b/commands_test.go @@ -782,7 +782,7 @@ func TestImagesTree(t *testing.T) { "(?m)^ └─[0-9a-f]+", "(?m)^ └─[0-9a-f]+", "(?m)^ └─[0-9a-f]+", - fmt.Sprintf(" └─%s Size: \\d+.\\d+ kB \\(virtual \\d+.\\d+ MB\\) Tags: test:latest", utils.TruncateID(image.ID)), + fmt.Sprintf(" └─%s Size: \\d+ B \\(virtual \\d+.\\d+ MB\\) Tags: test:latest", utils.TruncateID(image.ID)), } compiledRegexps := []*regexp.Regexp{} From 3108f0526ef25596c7f6f545b07c95d90e966a7d Mon Sep 17 00:00:00 2001 From: Marc Kuo Date: Tue, 5 Nov 2013 20:48:16 -0800 Subject: [PATCH 62/71] Add -p when running the container Without it, docker was not mapping/exposing the private port Docker version 0.6.5, build 3ff8459 --- docs/sources/examples/nodejs_web_app.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/examples/nodejs_web_app.rst b/docs/sources/examples/nodejs_web_app.rst index 19b10f6cb..68c073da7 100644 --- a/docs/sources/examples/nodejs_web_app.rst +++ b/docs/sources/examples/nodejs_web_app.rst @@ -176,11 +176,11 @@ Run the image +++++++++++++ Running your image with ``-d`` runs the container in detached mode, leaving the -container running in the background. Run the image you previously built: +container running in the background. The ``-p`` flag redirects a public port to a private port in the container. Run the image you previously built: .. code-block:: bash - sudo docker run -d /centos-node-hello + sudo docker run -p 49160:8080 -d /centos-node-hello Print the output of your app: From 74f853a2aeaf86ff675ecab5387bbd710a0138b7 Mon Sep 17 00:00:00 2001 From: Mark Allen Date: Tue, 5 Nov 2013 23:44:52 -0600 Subject: [PATCH 63/71] See if a path exists before injecting Reader there Closes #1965 --- container.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/container.go b/container.go index 92e05283d..50bf2ec67 100644 --- a/container.go +++ b/container.go @@ -390,11 +390,22 @@ func (settings *NetworkSettings) PortMappingAPI() []APIPort { // Inject the io.Reader at the given path. Note: do not close the reader func (container *Container) Inject(file io.Reader, pth string) error { + // Return error if path exists + if _, err := os.Stat(path.Join(container.rwPath(), pth)); err == nil { + // Since err is nil, the path could be stat'd and it exists + return fmt.Errorf("%s exists", pth) + } else if ! os.IsNotExist(err) { + // Expect err might be that the file doesn't exist, so + // if it's some other error, return that. + + return err + } + // Make sure the directory exists if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil { return err } - // FIXME: Handle permissions/already existing dest + dest, err := os.Create(path.Join(container.rwPath(), pth)) if err != nil { return err From 00f1398f7a8543e2448723635d93ea6ddf1eaadb Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 6 Nov 2013 07:37:43 -0800 Subject: [PATCH 64/71] Add debug to iptables --- iptables/iptables.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/iptables/iptables.go b/iptables/iptables.go index 8139b9881..82ecf8bb5 100644 --- a/iptables/iptables.go +++ b/iptables/iptables.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net" + "os" "os/exec" "strconv" "strings" @@ -122,10 +123,12 @@ func Raw(args ...string) ([]byte, error) { if err != nil { return nil, ErrIptablesNotFound } + if os.Getenv("DEBUG") != "" { + fmt.Printf("[DEBUG] [iptables]: %s, %v\n", path, args) + } output, err := exec.Command(path, args...).CombinedOutput() if err != nil { return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err) } return output, err - } From 16eba6bbb514fb4082e6779e175a29698a6734ce Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Wed, 6 Nov 2013 11:30:04 -0800 Subject: [PATCH 65/71] fix /images/json doc for 1.7 --- docs/sources/api/docker_remote_api_v1.7.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/sources/api/docker_remote_api_v1.7.rst b/docs/sources/api/docker_remote_api_v1.7.rst index d1b094c87..3985f7aa0 100644 --- a/docs/sources/api/docker_remote_api_v1.7.rst +++ b/docs/sources/api/docker_remote_api_v1.7.rst @@ -615,9 +615,7 @@ Copy files or folders from a container List Images *********** -.. http:get:: /images/(format) - - List images ``format`` could be json or viz (json default) +.. http:get:: /images/json **Example request**: From 360a694c52a48e0305317a18c59dc5ddc44859ad Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 6 Nov 2013 11:42:39 -0800 Subject: [PATCH 66/71] Bump version to v0.6.6 --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ VERSION | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baa1faac9..f5c961746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## 0.6.6 (2013-11-06) + +#### Runtime + +* Ensure container name on register +* Fix regression in /etc/hosts ++ Add lock around write operations in graph +* Check if port is valid +* Fix restart runtime error with ghost container networking ++ Added some more colors and animals to increase the pool of generated names +* Fix issues in docker inspect ++ Escape apparmor confinement ++ Set environment variables using a file. +* Prevent docker insert to erase something ++ Prevent DNS server conflicts in CreateBridgeIface ++ Validate bind mounts on the server side ++ Use parent image config in docker build +* Fix regression in /etc/hosts + +#### Client + ++ Add -P flag to publish all exposed ports ++ Add -notrunc and -q flags to docker history +* Fix docker commit, tag and import usage ++ Add stars, trusted builds and library flags in docker search +* Fix docker logs with tty + +#### RemoteAPI + +* Make /events API send headers immediately +* Do not split last column docker top ++ Add size to history + +#### Other + ++ Contrib: Desktop integration. Firefox usecase. ++ Dockerfile: bump to go1.2rc3 + ## 0.6.5 (2013-10-29) #### Runtime diff --git a/VERSION b/VERSION index 2a4fa8776..05e8a4593 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.5-dev +0.6.6 From 363738b8f21d02c2e4fac84bd2776c2d4a5e6755 Mon Sep 17 00:00:00 2001 From: Nate Jones Date: Wed, 6 Nov 2013 12:03:45 -0800 Subject: [PATCH 67/71] updating cli help doc for images --- docs/sources/commandline/cli.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index c064a3cef..6d56bccc3 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -315,8 +315,10 @@ Shell 1: (Again .. now showing events) List images -a=false: show all images + -notrunc=false: Don't truncate output -q=false: only show numeric IDs - -viz=false: output in graphviz format + -tree=false: output graph in tree format + -viz=false: output graph in graphviz format Displaying images visually ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 2d1155407a6e24b595d2627af6fa9f9f702483d2 Mon Sep 17 00:00:00 2001 From: andy diller Date: Wed, 6 Nov 2013 12:37:01 -0500 Subject: [PATCH 68/71] updated create, showing how to set expose port and bind to public port for ssh --- docs/sources/api/docker_remote_api_v1.6.rst | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/sources/api/docker_remote_api_v1.6.rst b/docs/sources/api/docker_remote_api_v1.6.rst index defe145d0..858547073 100644 --- a/docs/sources/api/docker_remote_api_v1.6.rst +++ b/docs/sources/api/docker_remote_api_v1.6.rst @@ -157,6 +157,57 @@ Create a container :statuscode 406: impossible to attach (container not running) :statuscode 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 startup**: + + .. sourcecode:: http + + POST /containers/create HTTP/1.1 + Content-Type: application/json + + { + "Cmd":[ + "/usr/sbin/sshd","-D" + ], + "Image":"image-with-sshd", + "ExposedPorts":{"22/tcp":{}} + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 201 OK + Content-Type: application/json + + { + "Id":"e90e34656806" + "Warnings":[] + } + + ** Second, start (using the ID returned above) the image we just created, mapping the ssh port 22 to something on the host**: + + .. sourcecode:: http + + POST /containers/e90e34656806/start HTTP/1.1 + Content-Type: application/json + + { + "PortBindings": { "22/tcp": [{ "HostPort": "11022" }]} + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 No Content + Content-Type: text/plain; charset=utf-8 + Content-Length: 0 + + ** Now you can ssh into your new container on port 11022.** + + + Inspect a container ******************* From 9990db2f5a762fdbc198cbfc5e96370d859739bb Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Wed, 6 Nov 2013 18:07:38 -0800 Subject: [PATCH 69/71] Fix bold --- docs/sources/api/docker_remote_api_v1.6.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/api/docker_remote_api_v1.6.rst b/docs/sources/api/docker_remote_api_v1.6.rst index 858547073..461ffefd6 100644 --- a/docs/sources/api/docker_remote_api_v1.6.rst +++ b/docs/sources/api/docker_remote_api_v1.6.rst @@ -185,7 +185,7 @@ Create a container "Warnings":[] } - ** Second, start (using the ID returned above) the image we just created, mapping the ssh port 22 to something on the host**: + **Second, start (using the ID returned above) the image we just created, mapping the ssh port 22 to something on the host**: .. sourcecode:: http @@ -204,7 +204,7 @@ Create a container Content-Type: text/plain; charset=utf-8 Content-Length: 0 - ** Now you can ssh into your new container on port 11022.** + **Now you can ssh into your new container on port 11022.** From 26f846bf779c97a69e85db356dff8c1fff76161c Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 7 Nov 2013 12:23:29 -0800 Subject: [PATCH 70/71] fix Assign id as name when all else fails --- runtime.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/runtime.go b/runtime.go index 0ef6786b2..671694fce 100644 --- a/runtime.go +++ b/runtime.go @@ -286,13 +286,12 @@ func (runtime *Runtime) restore() error { // Any containers that are left over do not exist in the graph for _, container := range containers { // Try to set the default name for a container if it exists prior to links - name, err := generateRandomName(runtime) + container.Name, err = generateRandomName(runtime) if err != nil { container.Name = container.ShortID() } - container.Name = name - if _, err := runtime.containerGraph.Set(name, container.ID); err != nil { + if _, err := runtime.containerGraph.Set(container.Name, container.ID); err != nil { utils.Debugf("Setting default id - %s", err) } register(container) From 6998c3c387518ba8e06ceff00b10be583c95d6cd Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 7 Nov 2013 12:19:24 -0800 Subject: [PATCH 71/71] go fmt --- config.go | 2 +- container.go | 6 ++-- docker/docker.go | 2 +- engine/engine.go | 28 +++++++-------- engine/init_test.go | 10 +++--- engine/job.go | 58 +++++++++++++++---------------- namesgenerator/names-generator.go | 2 +- netlink/netlink_darwin.go | 2 -- utils/daemon.go | 2 +- utils/random.go | 2 +- utils/utils.go | 4 +-- utils/utils_test.go | 26 +++++++------- utils_test.go | 2 +- 13 files changed, 71 insertions(+), 75 deletions(-) diff --git a/config.go b/config.go index 40c47e692..42ae23a90 100644 --- a/config.go +++ b/config.go @@ -1,8 +1,8 @@ package docker import ( - "net" "github.com/dotcloud/docker/engine" + "net" ) // FIXME: separate runtime configuration from http api configuration diff --git a/container.go b/container.go index 50bf2ec67..823862813 100644 --- a/container.go +++ b/container.go @@ -394,9 +394,9 @@ func (container *Container) Inject(file io.Reader, pth string) error { if _, err := os.Stat(path.Join(container.rwPath(), pth)); err == nil { // Since err is nil, the path could be stat'd and it exists return fmt.Errorf("%s exists", pth) - } else if ! os.IsNotExist(err) { + } else if !os.IsNotExist(err) { // Expect err might be that the file doesn't exist, so - // if it's some other error, return that. + // if it's some other error, return that. return err } @@ -1086,7 +1086,7 @@ func (container *Container) allocateNetwork() error { Gateway: manager.bridgeNetwork.IP, manager: manager, } - if iface !=nil && iface.IPNet.IP != nil { + if iface != nil && iface.IPNet.IP != nil { ipNum := ipToInt(iface.IPNet.IP) manager.ipAllocator.inUse[ipNum] = struct{}{} } else { diff --git a/docker/docker.go b/docker/docker.go index c500633a7..877e7f67d 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -4,9 +4,9 @@ import ( "flag" "fmt" "github.com/dotcloud/docker" + "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/sysinit" "github.com/dotcloud/docker/utils" - "github.com/dotcloud/docker/engine" "log" "os" "strings" diff --git a/engine/engine.go b/engine/engine.go index 8d67242ca..10cc07725 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -2,13 +2,12 @@ package engine import ( "fmt" - "os" - "log" - "runtime" "github.com/dotcloud/docker/utils" + "log" + "os" + "runtime" ) - type Handler func(*Job) string var globalHandlers map[string]Handler @@ -25,8 +24,8 @@ func Register(name string, handler Handler) error { // It acts as a store for *containers*, and allows manipulation of these // containers by executing *jobs*. type Engine struct { - root string - handlers map[string]Handler + root string + handlers map[string]Handler } // New initializes a new engine managing the directory specified at `root`. @@ -56,8 +55,8 @@ func New(root string) (*Engine, error) { return nil, err } eng := &Engine{ - root: root, - handlers: globalHandlers, + root: root, + handlers: globalHandlers, } return eng, nil } @@ -66,12 +65,12 @@ func New(root string) (*Engine, error) { // This function mimics `Command` from the standard os/exec package. func (eng *Engine) Job(name string, args ...string) *Job { job := &Job{ - eng: eng, - Name: name, - Args: args, - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, + eng: eng, + Name: name, + Args: args, + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, } handler, exists := eng.handlers[name] if exists { @@ -79,4 +78,3 @@ func (eng *Engine) Job(name string, args ...string) *Job { } return job } - diff --git a/engine/init_test.go b/engine/init_test.go index 5c03ded87..48d900917 100644 --- a/engine/init_test.go +++ b/engine/init_test.go @@ -1,18 +1,18 @@ package engine import ( - "testing" + "fmt" + "github.com/dotcloud/docker/utils" + "io/ioutil" "runtime" "strings" - "fmt" - "io/ioutil" - "github.com/dotcloud/docker/utils" + "testing" ) var globalTestID string func init() { - Register("dummy", func(job *Job) string { return ""; }) + Register("dummy", func(job *Job) string { return "" }) } func mkEngine(t *testing.T) *Engine { diff --git a/engine/job.go b/engine/job.go index 0bde2a0be..c2b14d55c 100644 --- a/engine/job.go +++ b/engine/job.go @@ -1,11 +1,11 @@ package engine import ( + "encoding/json" + "fmt" + "github.com/dotcloud/docker/utils" "io" "strings" - "fmt" - "encoding/json" - "github.com/dotcloud/docker/utils" ) // A job is the fundamental unit of work in the docker engine. @@ -20,17 +20,17 @@ import ( // One slight variation is that jobs report their status as a string. The // string "0" indicates success, and any other strings indicates an error. // This allows for richer error reporting. -// +// type Job struct { - eng *Engine - Name string - Args []string - env []string - Stdin io.ReadCloser - Stdout io.WriteCloser - Stderr io.WriteCloser - handler func(*Job) string - status string + eng *Engine + Name string + Args []string + env []string + Stdin io.ReadCloser + Stdout io.WriteCloser + Stderr io.WriteCloser + handler func(*Job) string + status string } // Run executes the job and blocks until the job completes. @@ -57,21 +57,21 @@ func (job *Job) String() string { } func (job *Job) Getenv(key string) (value string) { - for _, kv := range job.env { - if strings.Index(kv, "=") == -1 { - continue - } - parts := strings.SplitN(kv, "=", 2) - if parts[0] != key { - continue - } - if len(parts) < 2 { - value = "" - } else { - value = parts[1] - } - } - return + for _, kv := range job.env { + if strings.Index(kv, "=") == -1 { + continue + } + parts := strings.SplitN(kv, "=", 2) + if parts[0] != key { + continue + } + if len(parts) < 2 { + value = "" + } else { + value = parts[1] + } + } + return } func (job *Job) GetenvBool(key string) (value bool) { @@ -109,5 +109,5 @@ func (job *Job) SetenvList(key string, value []string) error { } func (job *Job) Setenv(key, value string) { - job.env = append(job.env, key + "=" + value) + job.env = append(job.env, key+"="+value) } diff --git a/namesgenerator/names-generator.go b/namesgenerator/names-generator.go index 076bb1cba..c0dd26830 100644 --- a/namesgenerator/names-generator.go +++ b/namesgenerator/names-generator.go @@ -12,7 +12,7 @@ type NameChecker interface { var ( colors = [...]string{"white", "silver", "gray", "black", "blue", "green", "cyan", "yellow", "gold", "orange", "brown", "red", "violet", "pink", "magenta", "purple", "maroon", "crimson", "plum", "fuchsia", "lavender", "slate", "navy", "azure", "aqua", "olive", "teal", "lime", "beige", "tan", "sienna"} - animals = [...]string{"ant", "bear", "bird", "cat", "chicken", "cow", "deer", "dog", "donkey", "duck", "fish", "fox", "frog", "horse", "kangaroo", "koala", "lemur", "lion", "lizard", "monkey", "octopus", "pig", "shark", "sheep", "sloth", "spider", "squirrel", "tiger", "toad", "weasel", "whale", "wolf"} + animals = [...]string{"ant", "bear", "bird", "cat", "chicken", "cow", "deer", "dog", "donkey", "duck", "fish", "fox", "frog", "horse", "kangaroo", "koala", "lemur", "lion", "lizard", "monkey", "octopus", "pig", "shark", "sheep", "sloth", "spider", "squirrel", "tiger", "toad", "weasel", "whale", "wolf"} ) func GenerateRandomName(checker NameChecker) (string, error) { diff --git a/netlink/netlink_darwin.go b/netlink/netlink_darwin.go index becfb8775..9a972fe63 100644 --- a/netlink/netlink_darwin.go +++ b/netlink/netlink_darwin.go @@ -9,7 +9,6 @@ func NetworkGetRoutes() ([]*net.IPNet, error) { return nil, fmt.Errorf("Not implemented") } - func NetworkLinkAdd(name string, linkType string) error { return fmt.Errorf("Not implemented") } @@ -18,7 +17,6 @@ func NetworkLinkUp(iface *net.Interface) error { return fmt.Errorf("Not implemented") } - func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { return fmt.Errorf("Not implemented") } diff --git a/utils/daemon.go b/utils/daemon.go index 179ff90e1..871122ed5 100644 --- a/utils/daemon.go +++ b/utils/daemon.go @@ -1,10 +1,10 @@ package utils import ( - "os" "fmt" "io/ioutil" "log" + "os" "strconv" ) diff --git a/utils/random.go b/utils/random.go index d5c37e44d..d4d33c690 100644 --- a/utils/random.go +++ b/utils/random.go @@ -1,9 +1,9 @@ package utils import ( - "io" "crypto/rand" "encoding/hex" + "io" ) func RandomString() string { diff --git a/utils/utils.go b/utils/utils.go index 4941eae42..152c41d86 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -15,8 +15,8 @@ import ( "os" "os/exec" "path/filepath" - "runtime" "regexp" + "runtime" "strconv" "strings" "sync" @@ -904,7 +904,7 @@ func StripComments(input []byte, commentMarker []byte) []byte { return output } -// GetNameserversAsCIDR returns nameservers (if any) listed in +// GetNameserversAsCIDR returns nameservers (if any) listed in // /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") // This function's output is intended for net.ParseCIDR func GetNameserversAsCIDR(resolvConf []byte) []string { diff --git a/utils/utils_test.go b/utils/utils_test.go index f10bb40d3..5377c181c 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -453,20 +453,20 @@ search example.com`: {"1.2.3.4/32", "4.3.2.1/32"}, `search example.com`: {}, `nameserver 1.2.3.4 search example.com -nameserver 4.3.2.1`: []string{"1.2.3.4/32", "4.3.2.1/32"}, - ``: []string{}, - ` nameserver 1.2.3.4 `: []string{"1.2.3.4/32"}, - `search example.com +nameserver 4.3.2.1`: {"1.2.3.4/32", "4.3.2.1/32"}, + ``: {}, + ` nameserver 1.2.3.4 `: {"1.2.3.4/32"}, + `search example.com nameserver 1.2.3.4 -#nameserver 4.3.2.1`: []string{"1.2.3.4/32"}, - `search example.com -nameserver 1.2.3.4 # not 4.3.2.1`: []string{"1.2.3.4/32"}, - } { - test := GetNameserversAsCIDR([]byte(resolv)) - if !StrSlicesEqual(test, result) { - t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) - } - } +#nameserver 4.3.2.1`: {"1.2.3.4/32"}, + `search example.com +nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"}, + } { + test := GetNameserversAsCIDR([]byte(resolv)) + if !StrSlicesEqual(test, result) { + t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) + } + } } func StrSlicesEqual(a, b []string) bool { diff --git a/utils_test.go b/utils_test.go index 14cbd7c6b..87426f38e 100644 --- a/utils_test.go +++ b/utils_test.go @@ -67,7 +67,7 @@ func newTestRuntime(prefix string) (runtime *Runtime, err error) { } config := &DaemonConfig{ - Root: root, + Root: root, AutoRestart: false, } runtime, err = NewRuntimeFromDirectory(config)