diff --git a/components/engine/AUTHORS b/components/engine/AUTHORS index 60533c1cc2..f9e9ecdace 100644 --- a/components/engine/AUTHORS +++ b/components/engine/AUTHORS @@ -20,6 +20,7 @@ Antony Messerli Asbjørn Enge Barry Allard Ben Toews +Ben Wiklund Benoit Chesneau Bhiraj Butala Bouke Haarsma @@ -47,6 +48,7 @@ Daniel YC Lin Darren Coxall David Calavera David Sissitka +Dinesh Subhraveti Deni Bertovic Dominik Honnef Don Spaulding @@ -68,6 +70,7 @@ Francisco Souza Frederick F. Kautz IV Gabriel Monroy Gareth Rushgrove +Graydon Hoare Greg Thornton Guillaume J. Charmes Gurjeet Singh @@ -113,6 +116,7 @@ Kyle Conroy Laurie Voss Louis Opter Manuel Meurer +Manuel Woelker Marco Hennings Marcus Farkas Marcus Ramberg diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index 060a07c21e..55d8d756a8 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,5 +1,66 @@ # Changelog +## 0.7.3 (2013-01-02) + +#### Builder + ++ Update ADD to use the image cache, based on a hash of the added content +* Add error message for empty Dockerfile + +#### Documentation + +- Fix outdated link to the "Introduction" on www.docker.io ++ Update the docs to get wider when the screen does +- Add information about needing to install LXC when using raw binaries +* Update Fedora documentation to disentangle the docker and docker.io conflict +* Add a note about using the new `-mtu` flag in several GCE zones ++ Add FrugalWare installation instructions ++ Add a more complete example of `docker run` +- Fix API documentation for creating and starting Privileged containers +- Add missing "name" parameter documentation on "/containers/create" +* Add a mention of `lxc-checkconfig` as a way to check for some of the necessary kernel configuration +- Update the 1.8 API documentation with some additions that were added to the docs for 1.7 + +#### Hack + +- Add missing libdevmapper dependency to the packagers documentation +* Update minimum Go requirement to a hard line at Go 1.2+ +* Many minor improvements to the Vagrantfile ++ Add ability to customize dockerinit search locations when compiling (to be used very sparingly only by packagers of platforms who require a nonstandard location) ++ Add coverprofile generation reporting +- Add `-a` to our Go build flags, removing the need for recompiling the stdlib manually +* Update Dockerfile to be more canonical and have less spurious warnings during build +- Fix some miscellaneous `docker pull` progress bar display issues +* Migrate more miscellaneous packages under the "pkg" folder +* Update TextMate highlighting to automatically be enabled for files named "Dockerfile" +* Reorganize syntax highlighting files under a common "contrib/syntax" directory +* Update install.sh script (https://get.docker.io/) to not fail if busybox fails to download or run at the end of the Ubuntu/Debian installation +* Add support for container names in bash completion + +#### Packaging + ++ Add an official Docker client binary for Darwin (Mac OS X) +* Remove empty "Vendor" string and added "License" on deb package ++ Add a stubbed version of "/etc/default/docker" in the deb package + +#### Runtime + +* Update layer application to extract tars in place, avoiding file churn while handling whiteouts +- Fix permissiveness of mtime comparisons in tar handling (since GNU tar and Go tar do not yet support sub-second mtime precision) +* Reimplement `docker top` in pure Go to work more consistently, and even inside Docker-in-Docker (thus removing the shell injection vulnerability present in some versions of `lxc-ps`) ++ Update `-H unix://` to work similarly to `-H tcp://` by inserting the default values for missing portions +- Fix more edge cases regarding dockerinit and deleted or replaced docker or dockerinit files +* Update container name validation to include '.' +- Fix use of a symlink or non-absolute path as the argument to `-g` to work as expected +* Update to handle external mounts outside of LXC, fixing many small mounting quirks and making future execution backends and other features simpler +* Update to use proper box-drawing characters everywhere in `docker images -tree` +* Move MTU setting from LXC configuration to directly use netlink +* Add `-S` option to external tar invocation for more efficient spare file handling ++ Add arch/os info to User-Agent string, especially for registry requests ++ Add `-mtu` option to Docker daemon for configuring MTU +- Fix `docker build` to exit with a non-zero exit code on error ++ Add `DOCKER_HOST` environment variable to configure the client `-H` flag without specifying it manually for every invocation + ## 0.7.2 (2013-12-16) #### Runtime @@ -15,7 +76,7 @@ - Prevent deletion of image if ANY container is depending on it even if the container is not running * Update docker push to use new progress display * Use os.Lstat to allow mounting unix sockets when inspecting volumes -- Adjusted handling of inactive user login +- Adjust handling of inactive user login - Add missing defines in devicemapper for older kernels - Allow untag operations with no container validation - Add auth config to docker build @@ -110,7 +171,7 @@ #### Runtime -* Improved stability, fixes some race conditons +* Improve stability, fixes some race conditons * Skip the volumes mounted when deleting the volumes of container. * Fix layer size computation: handle hard links correctly * Use the work Path for docker cp CONTAINER:PATH @@ -153,7 +214,7 @@ + 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 ++ Add 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. @@ -307,7 +368,7 @@ * Improve network performance for VirtualBox * Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.) - Fix contrib/mkimage-debian.sh apt caching prevention -+ Added Dockerfile.tmLanguage to contrib ++ Add Dockerfile.tmLanguage to contrib * Configured FPM to make /etc/init/docker.conf a config file * Enable SSH Agent forwarding in Vagrant VM * Several small tweaks/fixes for contrib/mkimage-debian.sh @@ -421,7 +482,7 @@ * Mount /dev/shm as a tmpfs - Switch from http to https for get.docker.io * Let userland proxy handle container-bound traffic -* Updated the Docker CLI to specify a value for the "Host" header. +* Update the Docker CLI to specify a value for the "Host" header. - Change network range to avoid conflict with EC2 DNS - Reduce connect and read timeout when pinging the registry * Parallel pull @@ -617,7 +678,7 @@ + Builder: 'docker build git://URL' fetches and builds a remote git repository * Runtime: 'docker ps -s' optionally prints container size -* Tests: Improved and simplified +* Tests: improved and simplified - Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail. - Builder: fix a regression when using ADD with single regular file. @@ -632,7 +693,7 @@ + ADD of a local file will detect tar archives and unpack them * ADD improvements: use tar for copy + automatically unpack local archives * ADD uses tar/untar for copies instead of calling 'cp -ar' -* Fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented. +* Fix the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented. - Fix a bug which caused builds to fail if ADD was the first command * Nicer output for 'docker build' @@ -677,7 +738,7 @@ + Detect faulty DNS configuration and replace it with a public default + Allow docker run : + You can now specify public port (ex: -p 80:4500) -* Improved image removal to garbage-collect unreferenced parents +* Improve image removal to garbage-collect unreferenced parents #### Client @@ -731,7 +792,7 @@ #### Documentation -* Improved install instructions. +* Improve install instructions. ## 0.3.3 (2013-05-23) @@ -816,7 +877,7 @@ + Support for data volumes ('docker run -v=PATH') + Share data volumes between containers ('docker run -volumes-from') -+ Improved documentation ++ Improve documentation * Upgrade to Go 1.0.3 * Various upgrades to the dev environment for contributors @@ -872,7 +933,7 @@ - Add debian packaging - Documentation: installing on Arch Linux - Documentation: running Redis on docker -- Fixed lxc 0.9 compatibility +- Fix lxc 0.9 compatibility - Automatically load aufs module - Various bugfixes and stability improvements @@ -907,7 +968,7 @@ - Stabilize process management - Layers can include a commit message - Simplified 'docker attach' -- Fixed support for re-attaching +- Fix support for re-attaching - Various bugfixes and stability improvements - Auto-download at run - Auto-login on push diff --git a/components/engine/Dockerfile b/components/engine/Dockerfile index b06c6553e0..9da4e8f039 100644 --- a/components/engine/Dockerfile +++ b/components/engine/Dockerfile @@ -24,40 +24,32 @@ # docker-version 0.6.1 -FROM ubuntu:12.04 -MAINTAINER Solomon Hykes +FROM stackbrew/ubuntu:12.04 +MAINTAINER Tianon Gravi (@tianon) -# Build dependencies -RUN echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list -RUN apt-get update -RUN apt-get install -y -q curl -RUN apt-get install -y -q git -RUN apt-get install -y -q mercurial -RUN apt-get install -y -q build-essential libsqlite3-dev +# Add precise-backports to get s3cmd >= 1.1.0 (so we get ENV variable support in our .s3cfg) +RUN echo 'deb http://archive.ubuntu.com/ubuntu precise-backports main universe' > /etc/apt/sources.list.d/backports.list -# Install Go -RUN curl -s https://go.googlecode.com/files/go1.2.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 - -# Ubuntu stuff -RUN apt-get install -y -q ruby1.9.3 rubygems libffi-dev -RUN gem install --no-rdoc --no-ri fpm -RUN apt-get install -y -q reprepro dpkg-sig - -RUN apt-get install -y -q python-pip -RUN pip install s3cmd==1.1.0-beta3 -RUN pip install python-magic==0.4.6 -RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg - -# Runtime dependencies -RUN apt-get install -y -q iptables -RUN apt-get install -y -q lxc -RUN apt-get install -y -q aufs-tools +# Packaged dependencies +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq \ + apt-utils \ + aufs-tools \ + build-essential \ + curl \ + dpkg-sig \ + git \ + iptables \ + libsqlite3-dev \ + lxc \ + mercurial \ + reprepro \ + ruby1.9.1 \ + ruby1.9.1-dev \ + s3cmd=1.1.0* \ + --no-install-recommends # Get lvm2 source for compiling statically -RUN git clone https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout v2_02_103 +RUN git clone https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout -q v2_02_103 # see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags # note: we can't use "git clone -b" above because it requires at least git 1.7.10 to be able to use that on a tag instead of a branch and we only have 1.7.9.5 @@ -65,9 +57,26 @@ RUN git clone https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /u RUN cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper && make install_device-mapper # see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL +# Install Go +RUN curl -s https://go.googlecode.com/files/go1.2.src.tar.gz | tar -v -C /usr/local -xz +ENV PATH /usr/local/go/bin:$PATH +ENV GOPATH /go:/go/src/github.com/dotcloud/docker/vendor +RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1 + +# Compile Go for cross compilation +ENV DOCKER_CROSSPLATFORMS darwin/amd64 darwin/386 +# TODO add linux/386 and linux/arm +RUN cd /usr/local/go/src && bash -xc 'for platform in $DOCKER_CROSSPLATFORMS; do GOOS=${platform%/*} GOARCH=${platform##*/} ./make.bash --no-clean 2>&1; done' + # Grab Go's cover tool for dead-simple code coverage testing RUN go get code.google.com/p/go.tools/cmd/cover +# TODO replace FPM with some very minimal debhelper stuff +RUN gem install --no-rdoc --no-ri fpm --version 1.0.1 + +# Setup s3cmd config +RUN /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY' > /.s3cfg + VOLUME /var/lib/docker WORKDIR /go/src/github.com/dotcloud/docker diff --git a/components/engine/MAINTAINERS b/components/engine/MAINTAINERS index 96469c9814..ef3aeda493 100644 --- a/components/engine/MAINTAINERS +++ b/components/engine/MAINTAINERS @@ -3,4 +3,6 @@ Guillaume Charmes (@creack) Victor Vieux (@vieux) Michael Crosby (@crosbymichael) api.go: Victor Vieux (@vieux) +Dockerfile: Tianon Gravi (@tianon) +Makefile: Tianon Gravi (@tianon) Vagrantfile: Daniel Mizyrycki (@mzdaniel) diff --git a/components/engine/Makefile b/components/engine/Makefile index 21ff0dc57d..86b2a0207f 100644 --- a/components/engine/Makefile +++ b/components/engine/Makefile @@ -1,4 +1,4 @@ -.PHONY: all binary build default docs shell test +.PHONY: all binary build cross default docs shell test DOCKER_RUN_DOCKER := docker run -rm -i -t -privileged -e TESTFLAGS -v $(CURDIR)/bundles:/go/src/github.com/dotcloud/docker/bundles docker @@ -10,6 +10,9 @@ all: build binary: build $(DOCKER_RUN_DOCKER) hack/make.sh binary +cross: build + $(DOCKER_RUN_DOCKER) hack/make.sh binary cross + docs: docker build -t docker-docs docs && docker run -p 8000:8000 docker-docs diff --git a/components/engine/VERSION b/components/engine/VERSION index 7486fdbc50..f38fc5393f 100644 --- a/components/engine/VERSION +++ b/components/engine/VERSION @@ -1 +1 @@ -0.7.2 +0.7.3 diff --git a/components/engine/Vagrantfile b/components/engine/Vagrantfile index 013027ecde..54fc783c00 100644 --- a/components/engine/Vagrantfile +++ b/components/engine/Vagrantfile @@ -26,7 +26,7 @@ fi # Adding an apt gpg key is idempotent. wget -q -O - https://get.docker.io/gpg | apt-key add - -# Creating the docker.list file is idempotent, but it may overrite desired +# Creating the docker.list file is idempotent, but it may overwrite desired # settings if it already exists. This could be solved with md5sum but it # doesn't seem worth it. echo 'deb http://get.docker.io/ubuntu docker main' > \ @@ -41,7 +41,7 @@ apt-get install -q -y lxc-docker usermod -a -G docker "$user" tmp=`mktemp -q` && { - # Only install the backport kernel, don't bother upgrade if the backport is + # Only install the backport kernel, don't bother upgrading if the backport is # already installed. We want parse the output of apt so we need to save it # with 'tee'. NOTE: The installation of the kernel will trigger dkms to # install vboxguest if needed. diff --git a/components/engine/api.go b/components/engine/api.go index da1fa9edee..1128c7a2b7 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -10,7 +10,7 @@ import ( "fmt" "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/auth" - "github.com/dotcloud/docker/systemd" + "github.com/dotcloud/docker/pkg/systemd" "github.com/dotcloud/docker/utils" "github.com/gorilla/mux" "io" diff --git a/components/engine/archive/archive.go b/components/engine/archive/archive.go index 838f8f6e7f..f8fcf0b163 100644 --- a/components/engine/archive/archive.go +++ b/components/engine/archive/archive.go @@ -3,6 +3,8 @@ package archive import ( "archive/tar" "bytes" + "compress/gzip" + "compress/bzip2" "fmt" "github.com/dotcloud/docker/utils" "io" @@ -59,6 +61,43 @@ func DetectCompression(source []byte) Compression { return Uncompressed } +func xzDecompress(archive io.Reader) (io.Reader, error) { + args := []string{"xz", "-d", "-c", "-q"} + + return CmdStream(exec.Command(args[0], args[1:]...), archive, nil) +} + +func DecompressStream(archive io.Reader) (io.Reader, error) { + buf := make([]byte, 10) + totalN := 0 + for totalN < 10 { + n, err := archive.Read(buf[totalN:]) + if err != nil { + if err == io.EOF { + return nil, fmt.Errorf("Tarball too short") + } + return nil, err + } + totalN += n + utils.Debugf("[tar autodetect] n: %d", n) + } + compression := DetectCompression(buf) + wrap := io.MultiReader(bytes.NewReader(buf), archive) + + switch compression { + case Uncompressed: + return wrap, nil + case Gzip: + return gzip.NewReader(wrap) + case Bzip2: + return bzip2.NewReader(wrap), nil + case Xz: + return xzDecompress(wrap) + default: + return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension()) + } +} + func (compression *Compression) Flag() string { switch *compression { case Bzip2: @@ -110,7 +149,7 @@ func escapeName(name string) string { // Tar creates an archive from the directory at `path`, only including files whose relative // paths are included in `filter`. If `filter` is nil, then all files are included. func TarFilter(path string, options *TarOptions) (io.Reader, error) { - args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"} + args := []string{"tar", "-S", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"} if options.Includes == nil { options.Includes = []string{"."} } @@ -155,7 +194,7 @@ func TarFilter(path string, options *TarOptions) (io.Reader, error) { } } - return CmdStream(exec.Command(args[0], args[1:]...), &files, func() { + return CmdStream(exec.Command(args[0], args[1:]...), bytes.NewBufferString(files), func() { if tmpDir != "" { _ = os.RemoveAll(tmpDir) } @@ -189,7 +228,7 @@ func Untar(archive io.Reader, path string, options *TarOptions) error { compression := DetectCompression(buf) utils.Debugf("Archive compression detected: %s", compression.Extension()) - args := []string{"--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()} + args := []string{"-S", "--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()} if options != nil { for _, exclude := range options.Excludes { @@ -301,7 +340,7 @@ func CopyFileWithTar(src, dst string) error { // CmdStream executes a command, and returns its stdout as a stream. // If the command fails to run or doesn't complete successfully, an error // will be returned, including anything written on stderr. -func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) { +func CmdStream(cmd *exec.Cmd, input io.Reader, atEnd func()) (io.Reader, error) { if input != nil { stdin, err := cmd.StdinPipe() if err != nil { @@ -312,7 +351,7 @@ func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) { } // Write stdin if any go func() { - _, _ = stdin.Write([]byte(*input)) + io.Copy(stdin, input) stdin.Close() }() } diff --git a/components/engine/archive/changes.go b/components/engine/archive/changes.go index a4076fc0ad..8fe9ff2233 100644 --- a/components/engine/archive/changes.go +++ b/components/engine/archive/changes.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" "syscall" + "time" ) type ChangeType int @@ -34,6 +35,21 @@ func (change *Change) String() string { return fmt.Sprintf("%s %s", kind, change.Path) } +// Gnu tar and the go tar writer don't have sub-second mtime +// precision, which is problematic when we apply changes via tar +// files, we handle this by comparing for exact times, *or* same +// second count and either a or b having exactly 0 nanoseconds +func sameFsTime(a, b time.Time) bool { + return a == b || + (a.Unix() == b.Unix() && + (a.Nanosecond() == 0 || b.Nanosecond() == 0)) +} + +func sameFsTimeSpec(a, b syscall.Timespec) bool { + return a.Sec == b.Sec && + (a.Nsec == b.Nsec || a.Nsec == 0 || b.Nsec == 0) +} + func Changes(layers []string, rw string) ([]Change, error) { var changes []Change err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error { @@ -85,7 +101,7 @@ func Changes(layers []string, rw string) ([]Change, error) { // However, if it's a directory, maybe it wasn't actually modified. // If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar if stat.IsDir() && f.IsDir() { - if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() { + if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) { // Both directories are the same, don't record the change return nil } @@ -181,7 +197,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { oldStat.Rdev != newStat.Rdev || // Don't look at size for dirs, its not a good measure of change (oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) || - getLastModification(oldStat) != getLastModification(newStat) { + !sameFsTimeSpec(getLastModification(oldStat), getLastModification(newStat)) { change := Change{ Path: newChild.path(), Kind: ChangeModify, diff --git a/components/engine/archive/changes_test.go b/components/engine/archive/changes_test.go index 714ab71e2d..1302b76f47 100644 --- a/components/engine/archive/changes_test.go +++ b/components/engine/archive/changes_test.go @@ -258,48 +258,44 @@ func TestChangesDirsMutated(t *testing.T) { } func TestApplyLayer(t *testing.T) { - t.Skip("Skipping TestApplyLayer due to known failures") // Disable this for now as it is broken - return + src, err := ioutil.TempDir("", "docker-changes-test") + if err != nil { + t.Fatal(err) + } + createSampleDir(t, src) + defer os.RemoveAll(src) + dst := src + "-copy" + if err := copyDir(src, dst); err != nil { + t.Fatal(err) + } + mutateSampleDir(t, dst) + defer os.RemoveAll(dst) - // src, err := ioutil.TempDir("", "docker-changes-test") - // if err != nil { - // t.Fatal(err) - // } - // createSampleDir(t, src) - // dst := src + "-copy" - // if err := copyDir(src, dst); err != nil { - // t.Fatal(err) - // } - // mutateSampleDir(t, dst) + changes, err := ChangesDirs(dst, src) + if err != nil { + t.Fatal(err) + } - // changes, err := ChangesDirs(dst, src) - // if err != nil { - // t.Fatal(err) - // } + layer, err := ExportChanges(dst, changes) + if err != nil { + t.Fatal(err) + } - // layer, err := ExportChanges(dst, changes) - // if err != nil { - // t.Fatal(err) - // } + layerCopy, err := NewTempArchive(layer, "") + if err != nil { + t.Fatal(err) + } - // layerCopy, err := NewTempArchive(layer, "") - // if err != nil { - // t.Fatal(err) - // } + if err := ApplyLayer(src, layerCopy); err != nil { + t.Fatal(err) + } - // if err := ApplyLayer(src, layerCopy); err != nil { - // t.Fatal(err) - // } + changes2, err := ChangesDirs(src, dst) + if err != nil { + t.Fatal(err) + } - // changes2, err := ChangesDirs(src, dst) - // if err != nil { - // t.Fatal(err) - // } - - // if len(changes2) != 0 { - // t.Fatalf("Unexpected differences after re applying mutation: %v", changes) - // } - - // os.RemoveAll(src) - // os.RemoveAll(dst) + if len(changes2) != 0 { + t.Fatalf("Unexpected differences after reapplying mutation: %v", changes2) + } } diff --git a/components/engine/archive/diff.go b/components/engine/archive/diff.go index f44991ecb5..464d57a742 100644 --- a/components/engine/archive/diff.go +++ b/components/engine/archive/diff.go @@ -1,6 +1,9 @@ package archive import ( + "archive/tar" + "github.com/dotcloud/docker/utils" + "io" "os" "path/filepath" "strings" @@ -8,87 +11,181 @@ import ( "time" ) +// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. +// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major, +// then the top 12 bits of the minor +func mkdev(major int64, minor int64) uint32 { + return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff)) +} +func timeToTimespec(time time.Time) (ts syscall.Timespec) { + if time.IsZero() { + // Return UTIME_OMIT special value + ts.Sec = 0 + ts.Nsec = ((1 << 30) - 2) + return + } + return syscall.NsecToTimespec(time.UnixNano()) +} + // ApplyLayer parses a diff in the standard layer format from `layer`, and // applies it to the directory `dest`. func ApplyLayer(dest string, layer Archive) error { - // Poor man's diff applyer in 2 steps: + // We need to be able to set any perms + oldmask := syscall.Umask(0) + defer syscall.Umask(oldmask) - // Step 1: untar everything in place - if err := Untar(layer, dest, nil); err != nil { + layer, err := DecompressStream(layer) + if err != nil { return err } - modifiedDirs := make(map[string]*syscall.Stat_t) - addDir := func(file string) { - d := filepath.Dir(file) - if _, exists := modifiedDirs[d]; !exists { - if s, err := os.Lstat(d); err == nil { - if sys := s.Sys(); sys != nil { - if stat, ok := sys.(*syscall.Stat_t); ok { - modifiedDirs[d] = stat + tr := tar.NewReader(layer) + + var dirs []*tar.Header + + // Iterate through the files in the archive. + for { + hdr, err := tr.Next() + if err == io.EOF { + // end of tar archive + break + } + if err != nil { + return err + } + + // Normalize name, for safety and for a simple is-root check + hdr.Name = filepath.Clean(hdr.Name) + + if !strings.HasSuffix(hdr.Name, "/") { + // Not the root directory, ensure that the parent directory exists. + // This happened in some tests where an image had a tarfile without any + // parent directories. + parent := filepath.Dir(hdr.Name) + parentPath := filepath.Join(dest, parent) + if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { + err = os.MkdirAll(parentPath, 600) + if err != nil { + return err + } + } + } + + // Skip AUFS metadata dirs + if strings.HasPrefix(hdr.Name, ".wh..wh.") { + continue + } + + path := filepath.Join(dest, hdr.Name) + base := filepath.Base(path) + if strings.HasPrefix(base, ".wh.") { + originalBase := base[len(".wh."):] + originalPath := filepath.Join(filepath.Dir(path), originalBase) + if err := os.RemoveAll(originalPath); err != nil { + return err + } + } else { + // If path exits we almost always just want to remove and replace it. + // The only exception is when it is a directory *and* the file from + // the layer is also a directory. Then we want to merge them (i.e. + // just apply the metadata from the layer). + hasDir := false + if fi, err := os.Lstat(path); err == nil { + if fi.IsDir() && hdr.Typeflag == tar.TypeDir { + hasDir = true + } else { + if err := os.RemoveAll(path); err != nil { + return err + } + } + } + + switch hdr.Typeflag { + case tar.TypeDir: + if !hasDir { + err = os.Mkdir(path, os.FileMode(hdr.Mode)) + if err != nil { + return err + } + } + dirs = append(dirs, hdr) + + case tar.TypeReg, tar.TypeRegA: + // Source is regular file + file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode)) + if err != nil { + return err + } + if _, err := io.Copy(file, tr); err != nil { + file.Close() + return err + } + file.Close() + + case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: + mode := uint32(hdr.Mode & 07777) + switch hdr.Typeflag { + case tar.TypeBlock: + mode |= syscall.S_IFBLK + case tar.TypeChar: + mode |= syscall.S_IFCHR + case tar.TypeFifo: + mode |= syscall.S_IFIFO + } + + if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil { + return err + } + + case tar.TypeLink: + if err := os.Link(filepath.Join(dest, hdr.Linkname), path); err != nil { + return err + } + + case tar.TypeSymlink: + if err := os.Symlink(hdr.Linkname, path); err != nil { + return err + } + + default: + utils.Debugf("unhandled type %d\n", hdr.Typeflag) + } + + if err = syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil { + return err + } + + // There is no LChmod, so ignore mode for symlink. Also, this + // must happen after chown, as that can modify the file mode + if hdr.Typeflag != tar.TypeSymlink { + err = syscall.Chmod(path, uint32(hdr.Mode&07777)) + if err != nil { + return err + } + } + + // Directories must be handled at the end to avoid further + // file creation in them to modify the mtime + if hdr.Typeflag != tar.TypeDir { + ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} + // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and + if hdr.Typeflag != tar.TypeSymlink { + if err := syscall.UtimesNano(path, ts); err != nil { + return err + } + } else { + if err := LUtimesNano(path, ts); err != nil { + return err } } } } } - // Step 2: walk for whiteouts and apply them, removing them in the process - err := filepath.Walk(dest, func(fullPath string, f os.FileInfo, err error) error { - if err != nil { - if os.IsNotExist(err) { - // This happens in the case of whiteouts in parent dir removing a directory - // We just ignore it - return filepath.SkipDir - } - return err - } - - // Rebase path - path, err := filepath.Rel(dest, fullPath) - if err != nil { - return err - } - path = filepath.Join("/", path) - - // Skip AUFS metadata - if matched, err := filepath.Match("/.wh..wh.*", path); err != nil { - return err - } else if matched { - addDir(fullPath) - if err := os.RemoveAll(fullPath); err != nil { - return err - } - } - - filename := filepath.Base(path) - if strings.HasPrefix(filename, ".wh.") { - rmTargetName := filename[len(".wh."):] - rmTargetPath := filepath.Join(filepath.Dir(fullPath), rmTargetName) - - // Remove the file targeted by the whiteout - addDir(rmTargetPath) - if err := os.RemoveAll(rmTargetPath); err != nil { - return err - } - // Remove the whiteout itself - addDir(fullPath) - if err := os.RemoveAll(fullPath); err != nil { - return err - } - } - return nil - }) - if err != nil { - return err - } - - for k, v := range modifiedDirs { - lastAccess := getLastAccess(v) - lastModification := getLastModification(v) - aTime := time.Unix(lastAccess.Unix()) - mTime := time.Unix(lastModification.Unix()) - - if err := os.Chtimes(k, aTime, mTime); err != nil { + for _, hdr := range dirs { + path := filepath.Join(dest, hdr.Name) + ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} + if err := syscall.UtimesNano(path, ts); err != nil { return err } } diff --git a/components/engine/archive/stat_darwin.go b/components/engine/archive/stat_darwin.go index 53ae9dee2f..e041783ec6 100644 --- a/components/engine/archive/stat_darwin.go +++ b/components/engine/archive/stat_darwin.go @@ -9,3 +9,7 @@ func getLastAccess(stat *syscall.Stat_t) syscall.Timespec { func getLastModification(stat *syscall.Stat_t) syscall.Timespec { return stat.Mtimespec } + +func LUtimesNano(path string, ts []syscall.Timespec) error { + return nil +} diff --git a/components/engine/archive/stat_linux.go b/components/engine/archive/stat_linux.go index 50b4627c4a..2f7a520ccd 100644 --- a/components/engine/archive/stat_linux.go +++ b/components/engine/archive/stat_linux.go @@ -1,6 +1,9 @@ package archive -import "syscall" +import ( + "syscall" + "unsafe" +) func getLastAccess(stat *syscall.Stat_t) syscall.Timespec { return stat.Atim @@ -9,3 +12,21 @@ func getLastAccess(stat *syscall.Stat_t) syscall.Timespec { func getLastModification(stat *syscall.Stat_t) syscall.Timespec { return stat.Mtim } + +func LUtimesNano(path string, ts []syscall.Timespec) error { + // These are not currently available in syscall + AT_FDCWD := -100 + AT_SYMLINK_NOFOLLOW := 0x100 + + var _path *byte + _path, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + + if _, _, err := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(AT_FDCWD), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), uintptr(AT_SYMLINK_NOFOLLOW), 0, 0); err != 0 && err != syscall.ENOSYS { + return err + } + + return nil +} diff --git a/components/engine/auth/auth.go b/components/engine/auth/auth.go index e88fb908f9..770a6a0c0f 100644 --- a/components/engine/auth/auth.go +++ b/components/engine/auth/auth.go @@ -163,7 +163,7 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e loginAgainstOfficialIndex := serverAddress == IndexServerAddress() - // to avoid sending the server address to the server it should be removed before marshalled + // to avoid sending the server address to the server it should be removed before being marshalled authCopy := *authConfig authCopy.ServerAddress = "" @@ -254,11 +254,11 @@ func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig { // default to the index server return config.Configs[IndexServerAddress()] } - // if its not the index server there are three cases: + // if it's not the index server there are three cases: // - // 1. this is a full config url -> it should be used as is - // 2. it could be a full url, but with the wrong protocol - // 3. it can be the hostname optionally with a port + // 1. a full config url -> it should be used as is + // 2. a full url, but with the wrong protocol + // 3. a hostname, with an optional port // // as there is only one auth entry which is fully qualified we need to start // parsing and matching diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index 7d87a17d3a..4f72a73520 100644 --- a/components/engine/buildfile.go +++ b/components/engine/buildfile.go @@ -1,7 +1,10 @@ package docker import ( + "crypto/sha256" + "encoding/hex" "encoding/json" + "errors" "fmt" "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/auth" @@ -11,11 +14,17 @@ import ( "net/url" "os" "path" + "path/filepath" "reflect" "regexp" + "sort" "strings" ) +var ( + ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty") +) + type BuildFile interface { Build(io.Reader) (string, error) CmdFrom(string) error @@ -26,10 +35,13 @@ type buildFile struct { runtime *Runtime srv *Server - image string - maintainer string - config *Config - context string + image string + maintainer string + config *Config + + contextPath string + context *utils.TarSum + verbose bool utilizeCache bool rm bool @@ -87,6 +99,27 @@ func (b *buildFile) CmdMaintainer(name string) error { return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name)) } +// probeCache checks to see if image-caching is enabled (`b.utilizeCache`) +// and if so attempts to look up the current `b.image` and `b.config` pair +// in the current server `b.srv`. If an image is found, probeCache returns +// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there +// is any error, it returns `(false, err)`. +func (b *buildFile) probeCache() (bool, error) { + if b.utilizeCache { + if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { + return false, err + } else if cache != nil { + fmt.Fprintf(b.outStream, " ---> Using cache\n") + utils.Debugf("[BUILDER] Use cached version") + b.image = cache.ID + return true, nil + } else { + utils.Debugf("[BUILDER] Cache miss") + } + } + return false, nil +} + func (b *buildFile) CmdRun(args string) error { if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to run") @@ -104,17 +137,12 @@ func (b *buildFile) CmdRun(args string) error { utils.Debugf("Command to be executed: %v", b.config.Cmd) - if b.utilizeCache { - if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { - return err - } else if cache != nil { - fmt.Fprintf(b.outStream, " ---> Using cache\n") - utils.Debugf("[BUILDER] Use cached version") - b.image = cache.ID - return nil - } else { - utils.Debugf("[BUILDER] Cache miss") - } + hit, err := b.probeCache() + if err != nil { + return err + } + if hit { + return nil } cid, err := b.run() @@ -260,44 +288,27 @@ func (b *buildFile) CmdVolume(args string) error { return nil } -func (b *buildFile) addRemote(container *Container, orig, dest string) error { - file, err := utils.Download(orig) +func (b *buildFile) checkPathForAddition(orig string) error { + origPath := path.Join(b.contextPath, orig) + if !strings.HasPrefix(origPath, b.contextPath) { + return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath) + } + _, err := os.Stat(origPath) if err != nil { - return err + return fmt.Errorf("%s: no such file or directory", orig) } - defer file.Body.Close() - - // If the destination is a directory, figure out the filename. - if strings.HasSuffix(dest, "/") { - u, err := url.Parse(orig) - if err != nil { - return err - } - path := u.Path - if strings.HasSuffix(path, "/") { - path = path[:len(path)-1] - } - parts := strings.Split(path, "/") - filename := parts[len(parts)-1] - if filename == "" { - return fmt.Errorf("cannot determine filename from url: %s", u) - } - dest = dest + filename - } - - return container.Inject(file.Body, dest) + return nil } func (b *buildFile) addContext(container *Container, orig, dest string) error { - origPath := path.Join(b.context, orig) - destPath := path.Join(container.RootfsPath(), dest) + var ( + origPath = path.Join(b.contextPath, orig) + destPath = path.Join(container.RootfsPath(), dest) + ) // Preserve the trailing '/' if strings.HasSuffix(dest, "/") { destPath = destPath + "/" } - if !strings.HasPrefix(origPath, b.context) { - return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath) - } fi, err := os.Stat(origPath) if err != nil { return fmt.Errorf("%s: no such file or directory", orig) @@ -321,7 +332,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error { } func (b *buildFile) CmdAdd(args string) error { - if b.context == "" { + if b.context == nil { return fmt.Errorf("No context given. Impossible to use ADD") } tmp := strings.SplitN(args, " ", 2) @@ -341,8 +352,90 @@ func (b *buildFile) CmdAdd(args string) error { cmd := b.config.Cmd b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)} - b.config.Image = b.image + + // FIXME: do we really need this? + var ( + origPath = orig + destPath = dest + ) + + if utils.IsURL(orig) { + resp, err := utils.Download(orig) + if err != nil { + return err + } + tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote") + if err != nil { + return err + } + tmpFileName := path.Join(tmpDirName, "tmp") + tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if err != nil { + return err + } + defer os.RemoveAll(tmpDirName) + if _, err = io.Copy(tmpFile, resp.Body); err != nil { + return err + } + origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName)) + tmpFile.Close() + + // If the destination is a directory, figure out the filename. + if strings.HasSuffix(dest, "/") { + u, err := url.Parse(orig) + if err != nil { + return err + } + path := u.Path + if strings.HasSuffix(path, "/") { + path = path[:len(path)-1] + } + parts := strings.Split(path, "/") + filename := parts[len(parts)-1] + if filename == "" { + return fmt.Errorf("cannot determine filename from url: %s", u) + } + destPath = dest + filename + } + } + + if err := b.checkPathForAddition(origPath); err != nil { + return err + } + + // Hash path and check the cache + if b.utilizeCache { + var ( + hash string + sums = b.context.GetSums() + ) + if fi, err := os.Stat(path.Join(b.contextPath, origPath)); err != nil { + return err + } else if fi.IsDir() { + var subfiles []string + for file, sum := range sums { + if strings.HasPrefix(file, origPath) { + subfiles = append(subfiles, sum) + } + } + sort.Strings(subfiles) + hasher := sha256.New() + hasher.Write([]byte(strings.Join(subfiles, ","))) + hash = "dir:" + hex.EncodeToString(hasher.Sum(nil)) + } else { + hash = "file:" + sums[origPath] + } + b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", hash, dest)} + hit, err := b.probeCache() + if err != nil { + return err + } + if hit { + return nil + } + } + // Create the container and start it container, _, err := b.runtime.Create(b.config, "") if err != nil { @@ -355,14 +448,8 @@ func (b *buildFile) CmdAdd(args string) error { } defer container.Unmount() - if utils.IsURL(orig) { - if err := b.addRemote(container, orig, dest); err != nil { - return err - } - } else { - if err := b.addContext(container, orig, dest); err != nil { - return err - } + if err := b.addContext(container, origPath, destPath); err != nil { + return err } if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil { @@ -460,17 +547,12 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment} defer func(cmd []string) { b.config.Cmd = cmd }(cmd) - if b.utilizeCache { - if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { - return err - } else if cache != nil { - fmt.Fprintf(b.outStream, " ---> Using cache\n") - utils.Debugf("[BUILDER] Use cached version") - b.image = cache.ID - return nil - } else { - utils.Debugf("[BUILDER] Cache miss") - } + hit, err := b.probeCache() + if err != nil { + return err + } + if hit { + return nil } container, warnings, err := b.runtime.Create(b.config, "") @@ -511,17 +593,17 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { var lineContinuation = regexp.MustCompile(`\s*\\\s*\n`) func (b *buildFile) Build(context io.Reader) (string, error) { - // FIXME: @creack "name" is a terrible variable name - name, err := ioutil.TempDir("", "docker-build") + tmpdirPath, err := ioutil.TempDir("", "docker-build") if err != nil { return "", err } - if err := archive.Untar(context, name, nil); err != nil { + b.context = &utils.TarSum{Reader: context} + if err := archive.Untar(b.context, tmpdirPath, nil); err != nil { return "", err } - defer os.RemoveAll(name) - b.context = name - filename := path.Join(name, "Dockerfile") + defer os.RemoveAll(tmpdirPath) + b.contextPath = tmpdirPath + filename := path.Join(tmpdirPath, "Dockerfile") if _, err := os.Stat(filename); os.IsNotExist(err) { return "", fmt.Errorf("Can't build a directory with no Dockerfile") } @@ -529,6 +611,9 @@ func (b *buildFile) Build(context io.Reader) (string, error) { if err != nil { return "", err } + if len(fileBytes) == 0 { + return "", ErrDockerfileEmpty + } dockerfile := string(fileBytes) dockerfile = lineContinuation.ReplaceAllString(dockerfile, "") stepN := 0 diff --git a/components/engine/commands.go b/components/engine/commands.go index ef6dce6bd5..97bfda5194 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -12,8 +12,8 @@ import ( "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/engine" + "github.com/dotcloud/docker/pkg/term" "github.com/dotcloud/docker/registry" - "github.com/dotcloud/docker/term" "github.com/dotcloud/docker/utils" "io" "io/ioutil" @@ -238,6 +238,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error { } err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers) if jerr, ok := err.(*utils.JSONError); ok { + // If no error code is set, default to 1 + if jerr.Code == 0 { + jerr.Code = 1 + } return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code} } return err @@ -469,6 +473,13 @@ func (cli *DockerCli) CmdInfo(args ...string) error { fmt.Fprintf(cli.out, "LXC Version: %s\n", remoteInfo.Get("LXCVersion")) fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener")) fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion")) + + if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" { + fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1) + } + if initPath := remoteInfo.Get("InitPath"); initPath != "" { + fmt.Fprintf(cli.out, "Init Path: %s\n", initPath) + } } if len(remoteInfo.GetList("IndexServerAddress")) != 0 { @@ -1237,9 +1248,9 @@ func (cli *DockerCli) WalkTree(noTrunc bool, images *[]APIImages, byParent map[s cli.WalkTree(noTrunc, &subimages, byParent, prefix+" ", printNode) } } else { - printNode(cli, noTrunc, image, prefix+"|─") + printNode(cli, noTrunc, image, prefix+"├─") if subimages, exists := byParent[image.ID]; exists { - cli.WalkTree(noTrunc, &subimages, byParent, prefix+"| ", printNode) + cli.WalkTree(noTrunc, &subimages, byParent, prefix+"│ ", printNode) } } } @@ -1814,6 +1825,8 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co flVolumes.Set(dstDir) binds = append(binds, bind) flVolumes.Delete(bind) + } else if bind == "/" { + return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'") } } diff --git a/components/engine/commands_unit_test.go b/components/engine/commands_unit_test.go index 2eac5ce60d..e44d9a1854 100644 --- a/components/engine/commands_unit_test.go +++ b/components/engine/commands_unit_test.go @@ -128,7 +128,9 @@ func TestParseRunVolumes(t *testing.T) { t.Fatalf("Error parsing volume flags, without volume, no volume should be present. Received %v", config.Volumes) } - mustParse(t, "-v /") + if _, _, err := parse(t, "-v /"); err == nil { + t.Fatalf("Expected error, but got none") + } if _, _, err := parse(t, "-v /:/"); err == nil { t.Fatalf("Error parsing volume flags, `-v /:/` should fail but didn't") diff --git a/components/engine/config.go b/components/engine/config.go index 1f743f6290..5a6de7a873 100644 --- a/components/engine/config.go +++ b/components/engine/config.go @@ -18,6 +18,7 @@ type DaemonConfig struct { DefaultIp net.IP InterContainerCommunication bool GraphDriver string + Mtu int } // ConfigFromJob creates and returns a new DaemonConfig object @@ -41,5 +42,10 @@ func ConfigFromJob(job *engine.Job) *DaemonConfig { config.DefaultIp = net.ParseIP(job.Getenv("DefaultIp")) config.InterContainerCommunication = job.GetenvBool("InterContainerCommunication") config.GraphDriver = job.Getenv("GraphDriver") + if mtu := job.GetenvInt("Mtu"); mtu != -1 { + config.Mtu = mtu + } else { + config.Mtu = DefaultNetworkMtu + } return &config } diff --git a/components/engine/container.go b/components/engine/container.go index 206f2dfe63..532a697839 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -7,7 +7,8 @@ import ( "fmt" "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/graphdriver" - "github.com/dotcloud/docker/term" + "github.com/dotcloud/docker/mount" + "github.com/dotcloud/docker/pkg/term" "github.com/dotcloud/docker/utils" "github.com/kr/pty" "io" @@ -48,7 +49,6 @@ type Container struct { network *NetworkInterface NetworkSettings *NetworkSettings - SysInitPath string ResolvConfPath string HostnamePath string HostsPath string @@ -297,7 +297,11 @@ func (container *Container) generateEnvConfig(env []string) error { if err != nil { return err } - ioutil.WriteFile(container.EnvConfigPath(), data, 0600) + p, err := container.EnvConfigPath() + if err != nil { + return err + } + ioutil.WriteFile(p, data, 0600) return nil } @@ -578,6 +582,7 @@ func (container *Container) Start() (err error) { params = append(params, "-g", network.Gateway, "-i", fmt.Sprintf("%s/%d", network.IPAddress, network.IPPrefixLen), + "-mtu", strconv.Itoa(container.runtime.config.Mtu), ) } @@ -681,6 +686,45 @@ func (container *Container) Start() (err error) { } } + root := container.RootfsPath() + envPath, err := container.EnvConfigPath() + if err != nil { + return err + } + + // Mount docker specific files into the containers root fs + if err := mount.Mount(runtime.sysInitPath, path.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil { + return err + } + if err := mount.Mount(envPath, path.Join(root, "/.dockerenv"), "none", "bind,ro"); err != nil { + return err + } + if err := mount.Mount(container.ResolvConfPath, path.Join(root, "/etc/resolv.conf"), "none", "bind,ro"); err != nil { + return err + } + + if container.HostnamePath != "" && container.HostsPath != "" { + if err := mount.Mount(container.HostnamePath, path.Join(root, "/etc/hostname"), "none", "bind,ro"); err != nil { + return err + } + if err := mount.Mount(container.HostsPath, path.Join(root, "/etc/hosts"), "none", "bind,ro"); err != nil { + return err + } + } + + // Mount user specified volumes + + for r, v := range container.Volumes { + mountAs := "ro" + if container.VolumesRW[v] { + mountAs = "rw" + } + + if err := mount.Mount(v, path.Join(root, r), "none", fmt.Sprintf("bind,%s", mountAs)); err != nil { + return err + } + } + container.cmd = exec.Command(params[0], params[1:]...) // Setup logging of stdout and stderr to disk @@ -836,7 +880,7 @@ func (container *Container) createVolumes() error { volPath = path.Join(container.RootfsPath(), volPath) rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.RootfsPath()) if err != nil { - panic(err) + return err } if _, err := os.Stat(rootVolPath); err != nil { @@ -1358,6 +1402,32 @@ func (container *Container) GetImage() (*Image, error) { } func (container *Container) Unmount() error { + var ( + err error + root = container.RootfsPath() + mounts = []string{ + path.Join(root, "/.dockerinit"), + path.Join(root, "/.dockerenv"), + path.Join(root, "/etc/resolv.conf"), + } + ) + + if container.HostnamePath != "" && container.HostsPath != "" { + mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts")) + } + + for r := range container.Volumes { + mounts = append(mounts, path.Join(root, r)) + } + + for _, m := range mounts { + if lastError := mount.Unmount(m); lastError != nil { + err = lastError + } + } + if err != nil { + return err + } return container.runtime.Unmount(container) } @@ -1377,8 +1447,20 @@ func (container *Container) jsonPath() string { return path.Join(container.root, "config.json") } -func (container *Container) EnvConfigPath() string { - return path.Join(container.root, "config.env") +func (container *Container) EnvConfigPath() (string, error) { + p := path.Join(container.root, "config.env") + if _, err := os.Stat(p); err != nil { + if os.IsNotExist(err) { + f, err := os.Create(p) + if err != nil { + return "", err + } + f.Close() + } else { + return "", err + } + } + return p, nil } func (container *Container) lxcConfigPath() string { diff --git a/components/engine/contrib/completion/bash/docker b/components/engine/contrib/completion/bash/docker index 8e535285e1..f1a515d00a 100755 --- a/components/engine/contrib/completion/bash/docker +++ b/components/engine/contrib/completion/bash/docker @@ -4,7 +4,7 @@ # # This script provides supports completion of: # - commands and their options -# - container ids +# - container ids and names # - image repos and tags # - filepaths # @@ -25,21 +25,24 @@ __docker_containers_all() { local containers containers="$( docker ps -a -q )" - COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) ) + names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" + COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) } __docker_containers_running() { local containers containers="$( docker ps -q )" - COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) ) + names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" + COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) } __docker_containers_stopped() { local containers containers="$( comm -13 <(docker ps -q | sort -u) <(docker ps -a -q | sort -u) )" - COMPREPLY=( $( compgen -W "$containers" -- "$cur" ) ) + names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" + COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) } __docker_image_repos() @@ -70,8 +73,9 @@ __docker_containers_and_images() { local containers images containers="$( docker ps -a -q )" + names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" images="$( docker images | awk 'NR>1{print $1":"$2}' )" - COMPREPLY=( $( compgen -W "$images $containers" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "$images $names $containers" -- "$cur" ) ) __ltrim_colon_completions "$cur" } diff --git a/components/engine/contrib/mkimage-debootstrap.sh b/components/engine/contrib/mkimage-debootstrap.sh index f9992d6e3c..3f268b52da 100755 --- a/components/engine/contrib/mkimage-debootstrap.sh +++ b/components/engine/contrib/mkimage-debootstrap.sh @@ -144,9 +144,9 @@ if [ -z "$strictDebootstrap" ]; then echo 'force-unsafe-io' | sudo tee etc/dpkg/dpkg.cfg.d/02apt-speedup > /dev/null # we want to effectively run "apt-get clean" after every install to keep images small (see output of "apt-get clean -s" for context) { - aptGetClean='rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true' - echo 'DPkg::Post-Invoke { "'$aptGetClean'"; };' - echo 'APT::Update::Post-Invoke { "'$aptGetClean'"; };' + aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";' + echo "DPkg::Post-Invoke { ${aptGetClean} };" + echo "APT::Update::Post-Invoke { ${aptGetClean} };" echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";' } | sudo tee etc/apt/apt.conf.d/no-cache > /dev/null # and remove the translations, too diff --git a/components/engine/contrib/Dockerfile.tmLanguage/Dockerfile.YAML-tmLanguage b/components/engine/contrib/syntax/textmate/Dockerfile.YAML-tmLanguage similarity index 100% rename from components/engine/contrib/Dockerfile.tmLanguage/Dockerfile.YAML-tmLanguage rename to components/engine/contrib/syntax/textmate/Dockerfile.YAML-tmLanguage diff --git a/components/engine/contrib/Dockerfile.tmLanguage/Dockerfile.tmLanguage b/components/engine/contrib/syntax/textmate/Dockerfile.tmLanguage similarity index 94% rename from components/engine/contrib/Dockerfile.tmLanguage/Dockerfile.tmLanguage rename to components/engine/contrib/syntax/textmate/Dockerfile.tmLanguage index ce5bb5e917..fa8f38992e 100644 --- a/components/engine/contrib/Dockerfile.tmLanguage/Dockerfile.tmLanguage +++ b/components/engine/contrib/syntax/textmate/Dockerfile.tmLanguage @@ -4,6 +4,10 @@ name Dockerfile + fileTypes + + Dockerfile + patterns diff --git a/components/engine/contrib/Dockerfile.tmLanguage/MAINTAINERS b/components/engine/contrib/syntax/textmate/MAINTAINERS similarity index 100% rename from components/engine/contrib/Dockerfile.tmLanguage/MAINTAINERS rename to components/engine/contrib/syntax/textmate/MAINTAINERS diff --git a/components/engine/contrib/Dockerfile.tmLanguage/README.md b/components/engine/contrib/syntax/textmate/README.md similarity index 100% rename from components/engine/contrib/Dockerfile.tmLanguage/README.md rename to components/engine/contrib/syntax/textmate/README.md diff --git a/components/engine/contrib/vim-syntax/LICENSE b/components/engine/contrib/syntax/vim/LICENSE similarity index 100% rename from components/engine/contrib/vim-syntax/LICENSE rename to components/engine/contrib/syntax/vim/LICENSE diff --git a/components/engine/contrib/vim-syntax/README.md b/components/engine/contrib/syntax/vim/README.md similarity index 100% rename from components/engine/contrib/vim-syntax/README.md rename to components/engine/contrib/syntax/vim/README.md diff --git a/components/engine/contrib/vim-syntax/doc/dockerfile.txt b/components/engine/contrib/syntax/vim/doc/dockerfile.txt similarity index 100% rename from components/engine/contrib/vim-syntax/doc/dockerfile.txt rename to components/engine/contrib/syntax/vim/doc/dockerfile.txt diff --git a/components/engine/contrib/vim-syntax/ftdetect/dockerfile.vim b/components/engine/contrib/syntax/vim/ftdetect/dockerfile.vim similarity index 100% rename from components/engine/contrib/vim-syntax/ftdetect/dockerfile.vim rename to components/engine/contrib/syntax/vim/ftdetect/dockerfile.vim diff --git a/components/engine/contrib/vim-syntax/syntax/dockerfile.vim b/components/engine/contrib/syntax/vim/syntax/dockerfile.vim similarity index 100% rename from components/engine/contrib/vim-syntax/syntax/dockerfile.vim rename to components/engine/contrib/syntax/vim/syntax/dockerfile.vim diff --git a/components/engine/docker/docker.go b/components/engine/docker/docker.go index 3ee7be9876..2d7e04ce92 100644 --- a/components/engine/docker/docker.go +++ b/components/engine/docker/docker.go @@ -40,6 +40,7 @@ func main() { flInterContainerComm = flag.Bool("icc", true, "Enable inter-container communication") flGraphDriver = flag.String("s", "", "Force the docker runtime to use a specific storage driver") flHosts = docker.NewListOpts(docker.ValidateHost) + flMtu = flag.Int("mtu", docker.DefaultNetworkMtu, "Set the containers network mtu") ) flag.Var(&flDns, "dns", "Force docker to use specific DNS servers") flag.Var(&flHosts, "H", "Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise") @@ -51,8 +52,13 @@ func main() { return } if flHosts.Len() == 0 { - // If we do not have a host, default to unix socket - flHosts.Set(fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)) + defaultHost := os.Getenv("DOCKER_HOST") + + if defaultHost == "" || *flDaemon { + // If we do not have a host, default to unix socket + defaultHost = fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET) + } + flHosts.Set(defaultHost) } if *bridgeName != "" && *bridgeIp != "" { @@ -69,6 +75,7 @@ func main() { flag.Usage() return } + eng, err := engine.New(*flRoot) if err != nil { log.Fatal(err) @@ -86,6 +93,7 @@ func main() { job.Setenv("DefaultIp", *flDefaultIp) job.SetenvBool("InterContainerCommunication", *flInterContainerComm) job.Setenv("GraphDriver", *flGraphDriver) + job.SetenvInt("Mtu", *flMtu) if err := job.Run(); err != nil { log.Fatal(err) } diff --git a/components/engine/docs/README.md b/components/engine/docs/README.md index 768cabdbb1..3fdbad2ead 100644 --- a/components/engine/docs/README.md +++ b/components/engine/docs/README.md @@ -46,7 +46,7 @@ directory: * Linux: `pip install -r docs/requirements.txt` -* Mac OS X: `[sudo] pip-2.7 -r docs/requirements.txt` +* Mac OS X: `[sudo] pip-2.7 install -r docs/requirements.txt` ###Alternative Installation: Docker Container diff --git a/components/engine/docs/sources/api/docker_remote_api.rst b/components/engine/docs/sources/api/docker_remote_api.rst index 7cb7b323c1..b6615ad7d6 100644 --- a/components/engine/docs/sources/api/docker_remote_api.rst +++ b/components/engine/docs/sources/api/docker_remote_api.rst @@ -26,10 +26,10 @@ Docker Remote API 2. Versions =========== -The current version of the API is 1.7 +The current version of the API is 1.8 Calling /images//insert is the same as calling -/v1.7/images//insert +/v1.8/images//insert You can still call an old version of the api using /v1.0/images//insert diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index b6661bfdb6..ab452798b9 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst @@ -1078,7 +1078,7 @@ Monitor Docker's events .. sourcecode:: http - POST /events?since=1374067924 + GET /events?since=1374067924 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.4.rst b/components/engine/docs/sources/api/docker_remote_api_v1.4.rst index 0441e068d0..5c8884b16f 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.4.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.4.rst @@ -1122,7 +1122,7 @@ Monitor Docker's events .. sourcecode:: http - POST /events?since=1374067924 + GET /events?since=1374067924 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.5.rst b/components/engine/docs/sources/api/docker_remote_api_v1.5.rst index 8cd501e2a1..609fc6b056 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.5.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.5.rst @@ -1093,7 +1093,7 @@ Monitor Docker's events .. sourcecode:: http - POST /events?since=1374067924 + GET /events?since=1374067924 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.6.rst b/components/engine/docs/sources/api/docker_remote_api_v1.6.rst index 25be478a1b..df53275a4f 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.6.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.6.rst @@ -1228,7 +1228,7 @@ Monitor Docker's events .. sourcecode:: http - POST /events?since=1374067924 + GET /events?since=1374067924 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.7.rst b/components/engine/docs/sources/api/docker_remote_api_v1.7.rst index 9857948732..d47f672df0 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.7.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.7.rst @@ -122,7 +122,6 @@ Create a container "AttachStdout":true, "AttachStderr":true, "PortSpecs":null, - "Privileged": false, "Tty":false, "OpenStdin":false, "StdinOnce":false, @@ -136,10 +135,12 @@ Create a container "/tmp": {} }, "VolumesFrom":"", - "WorkingDir":"" - + "WorkingDir":"", + "ExposedPorts":{ + "22/tcp": {} + } } - + **Example response**: .. sourcecode:: http @@ -364,10 +365,11 @@ Start a container { "Binds":["/tmp:/tmp"], "LxcConf":{"lxc.utsname":"docker"}, - "PortBindings":null + "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, + "Privileged":false, "PublishAllPorts":false } - + Binds need to reference Volumes that were defined during container creation. **Example response**: @@ -1159,7 +1161,7 @@ Monitor Docker's events .. sourcecode:: http - POST /events?since=1374067924 + GET /events?since=1374067924 **Example response**: diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.8.rst b/components/engine/docs/sources/api/docker_remote_api_v1.8.rst index bc51e209ef..3fe5cd73e0 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.8.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.8.rst @@ -122,7 +122,6 @@ Create a container "AttachStdout":true, "AttachStderr":true, "PortSpecs":null, - "Privileged": false, "Tty":false, "OpenStdin":false, "StdinOnce":false, @@ -132,12 +131,16 @@ Create a container ], "Dns":null, "Image":"base", - "Volumes":{}, + "Volumes":{ + "/tmp": {} + }, "VolumesFrom":"", - "WorkingDir":"" - + "WorkingDir":"", + "ExposedPorts":{ + "22/tcp": {} + } } - + **Example response**: .. sourcecode:: http @@ -151,6 +154,7 @@ Create a container } :jsonparam config: the container's configuration + :query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``. :statuscode 201: no error :statuscode 404: no such container :statuscode 406: impossible to attach (container not running) @@ -377,7 +381,10 @@ Start a container { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"} + "LxcConf":{"lxc.utsname":"docker"}, + "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, + "PublishAllPorts":false, + "Privileged":false } **Example response**: @@ -1173,7 +1180,7 @@ Monitor Docker's events .. sourcecode:: http - POST /events?since=1374067924 + GET /events?since=1374067924 **Example response**: diff --git a/components/engine/docs/sources/api/registry_api.rst b/components/engine/docs/sources/api/registry_api.rst index 8803b70ba8..b5c36cc344 100644 --- a/components/engine/docs/sources/api/registry_api.rst +++ b/components/engine/docs/sources/api/registry_api.rst @@ -19,7 +19,8 @@ Docker Registry API - It doesn’t have a local database - It will be open-sourced at some point -We expect that there will be multiple registries out there. To help to grasp the context, here are some examples of registries: +We expect that there will be multiple registries out there. To help to grasp +the context, here are some examples of registries: - **sponsor registry**: such a registry is provided by a third-party hosting infrastructure as a convenience for their customers and the docker community as a whole. Its costs are supported by the third party, but the management and operation of the registry are supported by dotCloud. It features read/write access, and delegates authentication and authorization to the Index. - **mirror registry**: such a registry is provided by a third-party hosting infrastructure but is targeted at their customers only. Some mechanism (unspecified to date) ensures that public images are pulled from a sponsor registry to the mirror registry, to make sure that the customers of the third-party provider can “docker pull” those images locally. @@ -37,7 +38,10 @@ We expect that there will be multiple registries out there. To help to grasp the - local mount point; - remote docker addressed through SSH. -The latter would only require two new commands in docker, e.g. “registryget” and “registryput”, wrapping access to the local filesystem (and optionally doing consistency checks). Authentication and authorization are then delegated to SSH (e.g. with public keys). +The latter would only require two new commands in docker, e.g. ``registryget`` +and ``registryput``, wrapping access to the local filesystem (and optionally +doing consistency checks). Authentication and authorization are then delegated +to SSH (e.g. with public keys). 2. Endpoints ============ diff --git a/components/engine/docs/sources/api/registry_index_spec.rst b/components/engine/docs/sources/api/registry_index_spec.rst index 449569414e..89f6319f5c 100644 --- a/components/engine/docs/sources/api/registry_index_spec.rst +++ b/components/engine/docs/sources/api/registry_index_spec.rst @@ -15,11 +15,13 @@ Registry & Index Spec --------- The Index is responsible for centralizing information about: + - User accounts - Checksums of the images - Public namespaces The Index has different components: + - Web UI - Meta-data store (comments, stars, list public repositories) - Authentication service @@ -27,7 +29,7 @@ The Index has different components: The index is authoritative for those information. -We expect that there will be only one instance of the index, run and managed by dotCloud. +We expect that there will be only one instance of the index, run and managed by Docker Inc. 1.2 Registry ------------ @@ -53,12 +55,16 @@ We expect that there will be multiple registries out there. To help to grasp the - local mount point; - remote docker addressed through SSH. -The latter would only require two new commands in docker, e.g. “registryget” and “registryput”, wrapping access to the local filesystem (and optionally doing consistency checks). Authentication and authorization are then delegated to SSH (e.g. with public keys). +The latter would only require two new commands in docker, e.g. ``registryget`` +and ``registryput``, wrapping access to the local filesystem (and optionally +doing consistency checks). Authentication and authorization are then delegated +to SSH (e.g. with public keys). 1.3 Docker ---------- On top of being a runtime for LXC, Docker is the Registry client. It supports: + - Push / Pull on the registry - Client authentication on the Index @@ -72,21 +78,33 @@ On top of being a runtime for LXC, Docker is the Registry client. It supports: 1. Contact the Index to know where I should download “samalba/busybox” 2. Index replies: - a. “samalba/busybox” is on Registry A - b. here are the checksums for “samalba/busybox” (for all layers) + a. ``samalba/busybox`` is on Registry A + b. here are the checksums for ``samalba/busybox`` (for all layers) c. token -3. Contact Registry A to receive the layers for “samalba/busybox” (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location. +3. Contact Registry A to receive the layers for ``samalba/busybox`` (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location. 4. registry contacts index to verify if token/user is allowed to download images 5. Index returns true/false lettings registry know if it should proceed or error out 6. Get the payload for all layers -It’s possible to run docker pull \https:///repositories/samalba/busybox. In this case, docker bypasses the Index. However the security is not guaranteed (in case Registry A is corrupted) because there won’t be any checksum checks. +It's possible to run: -Currently registry redirects to s3 urls for downloads, going forward all downloads need to be streamed through the registry. The Registry will then abstract the calls to S3 by a top-level class which implements sub-classes for S3 and local storage. +.. code-block:: bash -Token is only returned when the 'X-Docker-Token' header is sent with request. + docker pull https:///repositories/samalba/busybox -Basic Auth is required to pull private repos. Basic auth isn't required for pulling public repos, but if one is provided, it needs to be valid and for an active account. +In this case, Docker bypasses the Index. However the security is not guaranteed +(in case Registry A is corrupted) because there won’t be any checksum checks. + +Currently registry redirects to s3 urls for downloads, going forward all +downloads need to be streamed through the registry. The Registry will then +abstract the calls to S3 by a top-level class which implements sub-classes for +S3 and local storage. + +Token is only returned when the ``X-Docker-Token`` header is sent with request. + +Basic Auth is required to pull private repos. Basic auth isn't required for +pulling public repos, but if one is provided, it needs to be valid and for an +active account. API (pulling repository foo/bar): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -155,7 +173,9 @@ API (pulling repository foo/bar): **Index can be replaced!** For a private Registry deployed, a custom Index can be used to serve and validate token according to different policies. -Docker computes the checksums and submit them to the Index at the end of the push. When a repository name does not have checksums on the Index, it means that the push is in progress (since checksums are submitted at the end). +Docker computes the checksums and submit them to the Index at the end of the +push. When a repository name does not have checksums on the Index, it means +that the push is in progress (since checksums are submitted at the end). API (pushing repos foo/bar): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -237,10 +257,11 @@ API (pushing repos foo/bar): 2.3 Delete ---------- -If you need to delete something from the index or registry, we need a nice clean way to do that. Here is the workflow. +If you need to delete something from the index or registry, we need a nice +clean way to do that. Here is the workflow. -1. Docker contacts the index to request a delete of a repository “samalba/busybox” (authentication required with user credentials) -2. If authentication works and repository is valid, “samalba/busybox” is marked as deleted and a temporary token is returned +1. Docker contacts the index to request a delete of a repository ``samalba/busybox`` (authentication required with user credentials) +2. If authentication works and repository is valid, ``samalba/busybox`` is marked as deleted and a temporary token is returned 3. Send a delete request to the registry for the repository (along with the token) 4. Registry A contacts the Index to verify the token (token must corresponds to the repository name) 5. Index validates the token. Registry A deletes the repository and everything associated to it. @@ -312,24 +333,40 @@ The Index has two main purposes (along with its fancy social features): 3.1 Without an Index -------------------- -Using the Registry without the Index can be useful to store the images on a private network without having to rely on an external entity controlled by dotCloud. -In this case, the registry will be launched in a special mode (--standalone? --no-index?). In this mode, the only thing which changes is that Registry will never contact the Index to verify a token. It will be the Registry owner responsibility to authenticate the user who pushes (or even pulls) an image using any mechanism (HTTP auth, IP based, etc...). +Using the Registry without the Index can be useful to store the images on a +private network without having to rely on an external entity controlled by +Docker Inc. -In this scenario, the Registry is responsible for the security in case of data corruption since the checksums are not delivered by a trusted entity. +In this case, the registry will be launched in a special mode (--standalone? +--no-index?). In this mode, the only thing which changes is that Registry will +never contact the Index to verify a token. It will be the Registry owner +responsibility to authenticate the user who pushes (or even pulls) an image +using any mechanism (HTTP auth, IP based, etc...). -As hinted previously, a standalone registry can also be implemented by any HTTP server handling GET/PUT requests (or even only GET requests if no write access is necessary). +In this scenario, the Registry is responsible for the security in case of data +corruption since the checksums are not delivered by a trusted entity. + +As hinted previously, a standalone registry can also be implemented by any HTTP +server handling GET/PUT requests (or even only GET requests if no write access +is necessary). 3.2 With an Index ----------------- The Index data needed by the Registry are simple: + - Serve the checksums - Provide and authorize a Token -In the scenario of a Registry running on a private network with the need of centralizing and authorizing, it’s easy to use a custom Index. +In the scenario of a Registry running on a private network with the need of +centralizing and authorizing, it’s easy to use a custom Index. -The only challenge will be to tell Docker to contact (and trust) this custom Index. Docker will be configurable at some point to use a specific Index, it’ll be the private entity responsibility (basically the organization who uses Docker in a private environment) to maintain the Index and the Docker’s configuration among its consumers. +The only challenge will be to tell Docker to contact (and trust) this custom +Index. Docker will be configurable at some point to use a specific Index, it’ll +be the private entity responsibility (basically the organization who uses +Docker in a private environment) to maintain the Index and the Docker’s +configuration among its consumers. 4. The API ========== @@ -339,16 +376,22 @@ The first version of the api is available here: https://github.com/jpetazzo/dock 4.1 Images ---------- -The format returned in the images is not defined here (for layer and json), basically because Registry stores exactly the same kind of information as Docker uses to manage them. +The format returned in the images is not defined here (for layer and JSON), +basically because Registry stores exactly the same kind of information as +Docker uses to manage them. -The format of ancestry is a line-separated list of image ids, in age order. I.e. the image’s parent is on the last line, the parent of the parent on the next-to-last line, etc.; if the image has no parent, the file is empty. +The format of ancestry is a line-separated list of image ids, in age order, +i.e. the image’s parent is on the last line, the parent of the parent on the +next-to-last line, etc.; if the image has no parent, the file is empty. -GET /v1/images//layer -PUT /v1/images//layer -GET /v1/images//json -PUT /v1/images//json -GET /v1/images//ancestry -PUT /v1/images//ancestry +.. code-block:: bash + + GET /v1/images//layer + PUT /v1/images//layer + GET /v1/images//json + PUT /v1/images//json + GET /v1/images//ancestry + PUT /v1/images//ancestry 4.2 Users --------- @@ -393,7 +436,9 @@ PUT /v1/users/ 4.2.3 Login (Index) ^^^^^^^^^^^^^^^^^^^ -Does nothing else but asking for a user authentication. Can be used to validate credentials. HTTP Basic Auth for now, maybe change in future. + +Does nothing else but asking for a user authentication. Can be used to validate +credentials. HTTP Basic Auth for now, maybe change in future. GET /v1/users @@ -405,7 +450,10 @@ GET /v1/users 4.3 Tags (Registry) ------------------- -The Registry does not know anything about users. Even though repositories are under usernames, it’s just a namespace for the registry. Allowing us to implement organizations or different namespaces per user later, without modifying the Registry’s API. +The Registry does not know anything about users. Even though repositories are +under usernames, it’s just a namespace for the registry. Allowing us to +implement organizations or different namespaces per user later, without +modifying the Registry’s API. The following naming restrictions apply: @@ -439,7 +487,10 @@ DELETE /v1/repositories///tags/ 4.4 Images (Index) ------------------ -For the Index to “resolve” the repository name to a Registry location, it uses the X-Docker-Endpoints header. In other terms, this requests always add a “X-Docker-Endpoints” to indicate the location of the registry which hosts this repository. +For the Index to “resolve” the repository name to a Registry location, it uses +the X-Docker-Endpoints header. In other terms, this requests always add a +``X-Docker-Endpoints`` to indicate the location of the registry which hosts this +repository. 4.4.1 Get the images ^^^^^^^^^^^^^^^^^^^^^ @@ -484,17 +535,20 @@ Return 202 OK ====================== It’s possible to chain Registries server for several reasons: + - Load balancing - Delegate the next request to another server -When a Registry is a reference for a repository, it should host the entire images chain in order to avoid breaking the chain during the download. +When a Registry is a reference for a repository, it should host the entire +images chain in order to avoid breaking the chain during the download. The Index and Registry use this mechanism to redirect on one or the other. Example with an image download: -On every request, a special header can be returned: -X-Docker-Endpoints: server1,server2 +On every request, a special header can be returned:: + + X-Docker-Endpoints: server1,server2 On the next request, the client will always pick a server from this list. @@ -504,7 +558,8 @@ On the next request, the client will always pick a server from this list. 6.1 On the Index ----------------- -The Index supports both “Basic” and “Token” challenges. Usually when there is a “401 Unauthorized”, the Index replies this:: +The Index supports both “Basic” and “Token” challenges. Usually when there is a +``401 Unauthorized``, the Index replies this:: 401 Unauthorized WWW-Authenticate: Basic realm="auth required",Token @@ -543,11 +598,13 @@ The Registry only supports the Token challenge:: 401 Unauthorized WWW-Authenticate: Token -The only way is to provide a token on “401 Unauthorized” responses:: +The only way is to provide a token on ``401 Unauthorized`` responses:: - Authorization: Token signature=123abc,repository=”foo/bar”,access=read + Authorization: Token signature=123abc,repository="foo/bar",access=read -Usually, the Registry provides a Cookie when a Token verification succeeded. Every time the Registry passes a Cookie, you have to pass it back the same cookie.:: +Usually, the Registry provides a Cookie when a Token verification succeeded. +Every time the Registry passes a Cookie, you have to pass it back the same +cookie.:: 200 OK Set-Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=×tamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="; Path=/; HttpOnly diff --git a/components/engine/docs/sources/commandline/cli.rst b/components/engine/docs/sources/commandline/cli.rst index 9b1db93556..67c8b06189 100644 --- a/components/engine/docs/sources/commandline/cli.rst +++ b/components/engine/docs/sources/commandline/cli.rst @@ -12,7 +12,7 @@ To list available commands, either run ``docker`` with no parameters or execute $ sudo docker Usage: docker [OPTIONS] COMMAND [arg...] - -H=[unix:///var/run/docker.sock]: tcp://host:port to bind/connect to or unix://path/to/socket to use + -H=[unix:///var/run/docker.sock]: tcp://[host[:port]] to bind/connect to or unix://[/path/to/socket] to use. When host=[0.0.0.0], port=[4243] or path=[/var/run/docker.sock] is omitted, default values are used. A self-sufficient runtime for linux containers. @@ -27,7 +27,7 @@ To list available commands, either run ``docker`` with no parameters or execute Usage of docker: -D=false: Enable debug mode - -H=[unix:///var/run/docker.sock]: Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise + -H=[unix:///var/run/docker.sock]: tcp://[host[:port]] to bind or unix://[/path/to/socket] to use. When host=[0.0.0.0], port=[4243] or path=[/var/run/docker.sock] is omitted, default values are used. -api-enable-cors=false: Enable CORS headers in the remote API -b="": Attach containers to a pre-existing network bridge; use 'none' to disable container networking -bip="": Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of -b @@ -37,19 +37,32 @@ To list available commands, either run ``docker`` with no parameters or execute -icc=true: Enable inter-container communication -ip="0.0.0.0": Default IP address to use when binding container ports -iptables=true: Disable docker's addition of iptables rules + -mtu=1500: Set the containers network mtu -p="/var/run/docker.pid": Path to use for daemon PID file -r=true: Restart previously running containers -s="": Force the docker runtime to use a specific storage driver -v=false: Print version information and quit -The docker daemon is the persistent process that manages containers. Docker uses the same binary for both the +The Docker daemon is the persistent process that manages containers. Docker uses the same binary for both the daemon and client. To run the daemon you provide the ``-d`` flag. -To force docker to use devicemapper as the storage driver, use ``docker -d -s devicemapper`` +To force Docker to use devicemapper as the storage driver, use ``docker -d -s devicemapper``. -To set the dns server for all docker containers, use ``docker -d -dns 8.8.8.8`` +To set the DNS server for all Docker containers, use ``docker -d -dns 8.8.8.8``. + +To run the daemon with debug output, use ``docker -d -D``. + +The docker client will also honor the ``DOCKER_HOST`` environment variable to set +the ``-H`` flag for the client. + +:: + + docker -H tcp://0.0.0.0:4243 ps + # or + export DOCKER_HOST="tcp://0.0.0.0:4243" + docker ps + # both are equal -To run the daemon with debug output, use ``docker -d -D`` .. _cli_attach: @@ -68,11 +81,11 @@ To run the daemon with debug output, use ``docker -d -D`` You can detach from the container again (and leave it running) with ``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of the Docker client when it quits. When you detach from the container's -process the exit code will be retuned to the client. +process the exit code will be returned to the client. -To stop a container, use ``docker stop`` +To stop a container, use ``docker stop``. -To kill the container, use ``docker kill`` +To kill the container, use ``docker kill``. .. _cli_attach_examples: @@ -128,12 +141,11 @@ Examples: -no-cache: Do not use the cache when building the image. -rm: Remove intermediate containers after a successful build -The files at PATH or URL are called the "context" of the build. The -build process may refer to any of the files in the context, for -example when using an :ref:`ADD ` instruction. When a -single ``Dockerfile`` is given as URL, then no context is set. When a -git repository is set as URL, then the repository is used as the -context +The files at ``PATH`` or ``URL`` are called the "context" of the build. The +build process may refer to any of the files in the context, for example when +using an :ref:`ADD ` instruction. When a single ``Dockerfile`` +is given as ``URL``, then no context is set. When a Git repository is set as +``URL``, then the repository is used as the context .. _cli_build_examples: @@ -168,13 +180,13 @@ Examples: ---> f52f38b7823e Successfully built f52f38b7823e -This example specifies that the PATH is ``.``, and so all the files in -the local directory get tar'd and sent to the Docker daemon. The PATH +This example specifies that the ``PATH`` is ``.``, and so all the files in +the local directory get tar'd and sent to the Docker daemon. The ``PATH`` specifies where to find the files for the "context" of the build on the Docker daemon. Remember that the daemon could be running on a -remote machine and that no parsing of the Dockerfile happens at the +remote machine and that no parsing of the ``Dockerfile`` happens at the client side (where you're running ``docker build``). That means that -*all* the files at PATH get sent, not just the ones listed to +*all* the files at ``PATH`` get sent, not just the ones listed to :ref:`ADD ` in the ``Dockerfile``. The transfer of context from the local machine to the Docker daemon is @@ -197,16 +209,16 @@ tag will be ``2.0`` This will read a ``Dockerfile`` from *stdin* without context. Due to the lack of a context, no contents of any local directory will be sent -to the ``docker`` daemon. Since there is no context, a Dockerfile +to the ``docker`` daemon. Since there is no context, a ``Dockerfile`` ``ADD`` only works if it refers to a remote URL. .. code-block:: bash $ sudo docker build github.com/creack/docker-firefox -This will clone the Github repository and use the cloned repository as +This will clone the GitHub repository and use the cloned repository as context. The ``Dockerfile`` at the root of the repository is used as -``Dockerfile``. Note that you can specify an arbitrary git repository +``Dockerfile``. Note that you can specify an arbitrary Git repository by using the ``git://`` schema. @@ -247,7 +259,7 @@ Change the command that a container runs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes you have an application container running just a service and you need -to make a quick change (run bash?) and then change it back. +to make a quick change and then change it back. In this example, we run a container with ``ls`` and then change the image to run ``ls /etc``. @@ -270,9 +282,9 @@ Full -run example The ``-run`` JSON hash changes the ``Config`` section when running ``docker inspect CONTAINERID`` or ``config`` when running ``docker inspect IMAGEID``. -(multiline is ok within a single quote ``'``) +(Multiline is okay within a single quote ``'``) -:: +.. code-block:: bash $ sudo docker commit -run=' { @@ -315,7 +327,7 @@ or ``config`` when running ``docker inspect IMAGEID``. Copy files/folders from the containers filesystem to the host path. Paths are relative to the root of the filesystem. - + .. code-block:: bash $ sudo docker cp 7bb0e258aefe:/etc/debian_version . @@ -329,7 +341,7 @@ or ``config`` when running ``docker inspect IMAGEID``. :: Usage: docker diff CONTAINER - + List the changed files and directories in a container's filesystem There are 3 events that are listed in the 'diff': @@ -338,7 +350,7 @@ There are 3 events that are listed in the 'diff': 2. ```D``` - Delete 3. ```C``` - Change -for example: +For example: .. code-block:: bash @@ -366,7 +378,7 @@ for example: Usage: docker events Get real time events from the server - + -since="": Show previously created events and then stream. (either seconds since epoch, or date string as below) @@ -429,8 +441,8 @@ Show events in the past from a specified time Usage: docker export CONTAINER Export the contents of a filesystem as a tar archive to STDOUT - -for example: + +For example: .. code-block:: bash @@ -450,7 +462,7 @@ for example: -notrunc=false: Don't truncate output -q=false: only show numeric IDs -To see how the docker:latest image was built: +To see how the ``docker:latest`` image was built: .. code-block:: bash @@ -482,7 +494,7 @@ To see how the docker:latest image was built: d5e85dc5b1d8 2 weeks ago /bin/sh -c apt-get update 13e642467c11 2 weeks ago /bin/sh -c echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt/sources.list ae6dde92a94e 2 weeks ago /bin/sh -c #(nop) MAINTAINER Solomon Hykes - ubuntu:12.04 6 months ago + ubuntu:12.04 6 months ago .. _cli_images: @@ -500,7 +512,7 @@ To see how the docker:latest image was built: -q=false: only show numeric IDs -tree=false: output graph in tree format -viz=false: output graph in graphviz format - + Listing the most recently created images ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -553,15 +565,15 @@ Displaying image hierarchy $ sudo docker images -tree - |─8dbd9e392a96 Size: 131.5 MB (virtual 131.5 MB) Tags: ubuntu:12.04,ubuntu:latest,ubuntu:precise + ├─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 + ├─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) @@ -588,10 +600,9 @@ Displaying image hierarchy (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it. At this time, the URL must start with ``http`` and point to a single -file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) containing a +file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, or .txz) containing a root filesystem. If you would like to import from a local directory or -archive, you can use the ``-`` parameter to take the data from -standard in. +archive, you can use the ``-`` parameter to take the data from *stdin*. Examples ~~~~~~~~ @@ -601,24 +612,30 @@ Import from a remote location This will create a new untagged image. -``$ sudo docker import http://example.com/exampleimage.tgz`` +.. code-block:: bash + + $ sudo docker import http://example.com/exampleimage.tgz Import from a local file ........................ -Import to docker via pipe and standard in +Import to docker via pipe and *stdin*. -``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new`` +.. code-block:: bash + + $ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new Import from a local directory ............................. -``$ sudo tar -c . | docker import - exampleimagedir`` +.. code-block:: bash -Note the ``sudo`` in this example -- you must preserve the ownership -of the files (especially root ownership) during the archiving with -tar. If you are not root (or sudo) when you tar, then the ownerships -might not get preserved. + $ sudo tar -c . | docker import - exampleimagedir + +Note the ``sudo`` in this example -- you must preserve the ownership of the +files (especially root ownership) during the archiving with tar. If you are not +root (or the sudo command) when you tar, then the ownerships might not get +preserved. .. _cli_info: @@ -657,16 +674,16 @@ might not get preserved. Insert a file from URL in the IMAGE at PATH -Use the specified IMAGE as the parent for a new image which adds a -:ref:`layer ` containing the new file. ``insert`` does not modify -the original image, and the new image has the contents of the parent image, -plus the new file. +Use the specified ``IMAGE`` as the parent for a new image which adds a +:ref:`layer ` containing the new file. The ``insert`` command does +not modify the original image, and the new image has the contents of the parent +image, plus the new file. Examples ~~~~~~~~ -Insert file from github +Insert file from GitHub ....................... .. code-block:: bash @@ -681,16 +698,16 @@ Insert file from github :: - Usage: docker inspect [OPTIONS] CONTAINER + Usage: docker inspect CONTAINER|IMAGE [CONTAINER|IMAGE...] - Return low-level information on a container + Return low-level information on a container/image - -format="": template to output results + -format="": Format the output using the given go template. By default, this will render all results in a JSON array. If a format is specified, the given template will be executed for each result. -Go's `text/template ` package +Go's `text/template `_ package describes all the details of the format. Examples @@ -795,14 +812,14 @@ Known Issues (kill) Fetch the logs of a container -``docker logs`` is a convenience which batch-retrieves whatever logs -are present at the time of execution. This does not guarantee -execution order when combined with a ``docker run`` (i.e. your run may -not have generated any logs at the time you execute ``docker logs``). +The ``docker logs`` command is a convenience which batch-retrieves whatever +logs are present at the time of execution. This does not guarantee execution +order when combined with a ``docker run`` (i.e. your run may not have generated +any logs at the time you execute ``docker logs``). -``docker logs -f`` combines ``docker logs`` and ``docker attach``: it -will first return all logs from the beginning and then continue -streaming new output from the container's stdout and stderr. +The ``docker logs -f`` command combines ``docker logs`` and ``docker attach``: +it will first return all logs from the beginning and then continue streaming +new output from the container's stdout and stderr. .. _cli_port: @@ -940,7 +957,7 @@ Removing tagged images ~~~~~~~~~~~~~~~~~~~~~~ Images can be removed either by their short or long ID's, or their image names. -If an image has more than one name, each of them needs to be removed before the +If an image has more than one name, each of them needs to be removed before the image is removed. .. code-block:: bash @@ -952,7 +969,7 @@ image is removed. test2 latest fd484f19954f 23 seconds ago 7 B (virtual 4.964 MB) $ sudo docker rmi fd484f19954f - Error: Conflict, fd484f19954f wasn't deleted + Error: Conflict, cannot delete image fd484f19954f because it is tagged in multiple repositories 2013/12/11 05:47:16 Error: failed to remove one or more images $ sudo docker rmi test1 @@ -1004,13 +1021,14 @@ image is removed. -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 - -``'docker run'`` first ``'creates'`` a writeable container layer over -the specified image, and then ``'starts'`` it using the specified -command. That is, ``'docker run'`` is equivalent to the API -``/containers/create`` then ``/containers/(id)/start``. -``docker run`` can be used in combination with ``docker commit`` to :ref:`change the command that a container runs `. +The ``docker run`` command first ``creates`` a writeable container layer over +the specified image, and then ``starts`` it using the specified command. That +is, ``docker run`` is equivalent to the API ``/containers/create`` then +``/containers/(id)/start``. + +The ``docker run`` command can be used in combination with ``docker commit`` to +:ref:`change the command that a container runs `. Known Issues (run -volumes-from) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1026,10 +1044,10 @@ Examples: $ sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test" -This will create a container and print "test" to the console. The -``cidfile`` flag makes docker attempt to create a new file and write the -container ID to it. If the file exists already, docker will return an -error. Docker will close this file when docker run exits. +This will create a container and print ``test`` to the console. The +``cidfile`` flag makes Docker attempt to create a new file and write the +container ID to it. If the file exists already, Docker will return an +error. Docker will close this file when ``docker run`` exits. .. code-block:: bash @@ -1063,7 +1081,7 @@ use-cases, like running Docker within Docker. $ sudo 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 +here ``/path/to/dir/``. If the path does not exists it is created inside the container. .. code-block:: bash @@ -1080,7 +1098,7 @@ using the container, but inside the current working directory. $ sudo 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 +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. @@ -1114,11 +1132,31 @@ to the newly created container. $ sudo docker run -volumes-from 777f7dc92da7,ba8c0c54f0f2:ro -i -t ubuntu pwd The ``-volumes-from`` flag mounts all the defined volumes from the -refrence containers. Containers can be specified by a comma seperated +referenced containers. Containers can be specified by a comma seperated list or by repetitions of the ``-volumes-from`` argument. The container -id may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in +ID may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in read-only or read-write mode, respectively. By default, the volumes are mounted -in the same mode (rw or ro) as the reference container. +in the same mode (read write or read only) as the reference container. + +A complete example +.................. + +.. code-block:: bash + + $ sudo docker run -d -name static static-web-files sh + $ sudo docker run -d -expose=8098 -name riak riakserver + $ sudo docker run -d -m 100m -e DEVELOPMENT=1 -e BRANCH=example-code -v $(pwd):/app/bin:ro -name app appserver + $ sudo docker run -d -p 1443:443 -dns=dns.dev.org -v /var/log/httpd -volumes-from static -link riak -link app -h www.sven.dev.org -name web webserver + $ sudo docker run -t -i -rm -volumes-from web -w /var/log/httpd busybox tail -f access.log + +This example shows 5 containers that might be set up to test a web application change: + +1. Start a pre-prepared volume image ``static-web-files`` (in the background) that has CSS, image and static HTML in it, (with a ``VOLUME`` instruction in the ``Dockerfile`` to allow the web server to use those files); +2. Start a pre-prepared ``riakserver`` image, give the container name ``riak`` and expose port ``8098`` to any containers that link to it; +3. Start the ``appserver`` image, restricting its memory usage to 100MB, setting two environment variables ``DEVELOPMENT`` and ``BRANCH`` and bind-mounting the current directory (``$(pwd)``) in the container in read-only mode as ``/app/bin``; +4. Start the ``webserver``, mapping port ``443`` in the container to port ``1443`` on the Docker server, setting the DNS server to ``dns.dev.org``, creating a volume to put the log files into (so we can access it from another container), then importing the files from the volume exposed by the ``static`` container, and linking to all exposed ports from ``riak`` and ``app``. Lastly, we set the hostname to ``web.sven.dev.org`` so its consistent with the pre-generated SSL certificate; +5. Finally, we create a container that runs ``tail -f access.log`` using the logs volume from the ``web`` container, setting the workdir to ``/var/log/httpd``. The ``-rm`` option means that when the container exits, the container's layer is removed. + .. _cli_save: @@ -1205,7 +1243,7 @@ The main process inside the container will receive SIGTERM, and after a grace pe ``version`` ----------- -Show the version of the docker client, daemon, and latest released version. +Show the version of the Docker client, daemon, and latest released version. .. _cli_wait: diff --git a/components/engine/docs/sources/contributing/devenvironment.rst b/components/engine/docs/sources/contributing/devenvironment.rst index cc254bbc68..6675173584 100644 --- a/components/engine/docs/sources/contributing/devenvironment.rst +++ b/components/engine/docs/sources/contributing/devenvironment.rst @@ -136,7 +136,7 @@ You can run an interactive session in the newly built container: Extra Step: Build and view the Documentation -------------------------------------------- +-------------------------------------------- If you want to read the documentation from a local website, or are making changes to it, you can build the documentation and then serve it by: diff --git a/components/engine/docs/sources/examples/running_ssh_service.rst b/components/engine/docs/sources/examples/running_ssh_service.rst index 3d0a782678..a0ce532d8d 100644 --- a/components/engine/docs/sources/examples/running_ssh_service.rst +++ b/components/engine/docs/sources/examples/running_ssh_service.rst @@ -94,5 +94,13 @@ The password is ``screencast``. $ ifconfig $ ssh root@192.168.33.10 -p 49154 # Thanks for watching, Thatcher thatcher@dotcloud.com + +Update: +------- + +For Ubuntu 13.10 using stackbrew/ubuntu, you may need do these additional steps: + +1. change /etc/pam.d/sshd, pam_loginuid line 'required' to 'optional' +2. echo LANG=\"en_US.UTF-8\" > /etc/default/locale diff --git a/components/engine/docs/sources/faq.rst b/components/engine/docs/sources/faq.rst index 23460f4b55..e2e16c362b 100644 --- a/components/engine/docs/sources/faq.rst +++ b/components/engine/docs/sources/faq.rst @@ -111,7 +111,7 @@ What does Docker add to just plain LXC? registry to store and transfer private containers, for internal server deployments for example. - * *Tool ecosystem.* + * *Tool ecosystem.* Docker defines an API for automating and customizing the creation and deployment of containers. There are a huge number of tools integrating with Docker to extend its @@ -122,6 +122,11 @@ What does Docker add to just plain LXC? (Jenkins, Strider, Travis), etc. Docker is rapidly establishing itself as the standard for container-based tooling. +What is different between a Docker container and a VM? +...................................................... + +There's a great StackOverflow answer `showing the differences `_. + Do I lose my data when the container exits? ........................................... @@ -129,6 +134,53 @@ Not at all! Any data that your application writes to disk gets preserved in its container until you explicitly delete the container. The file system for the container persists even after the container halts. +How far do Docker containers scale? +................................... + +Some of the largest server farms in the world today are based on containers. +Large web deployments like Google and Twitter, and platform providers such as +Heroku and dotCloud all run on container technology, at a scale of hundreds of +thousands or even millions of containers running in parallel. + +How do I connect Docker containers? +................................... + +Currently the recommended way to link containers is via the `link` primitive. +You can see details of how to `work with links here +`_. + +Also of useful when enabling more flexible service portability is the +`Ambassador linking pattern +`_. + +How do I run more than one process in a Docker container? +......................................................... + +Any capable process supervisor such as http://supervisord.org/, runit, s6, or +daemontools can do the trick. Docker will start up the process management +daemon which will then fork to run additional processes. As long as the +processor manager daemon continues to run, the container will continue to as +well. You can see a more substantial example `that uses supervisord here +`_. + +What platforms does Docker run on? +.................................. + +Linux: + +- Ubuntu 12.04, 13.04 et al +- Fedora 19/20+ +- RHEL 6.5+ +- Centos 6+ +- Gento +- ArchLinux + +Cloud: + +- Amazon EC2 +- Google Compute Engine +- Rackspace + Can I help by adding some questions and answers? ................................................ diff --git a/components/engine/docs/sources/index.rst b/components/engine/docs/sources/index.rst index 88752ac3bf..1fb82f3bec 100644 --- a/components/engine/docs/sources/index.rst +++ b/components/engine/docs/sources/index.rst @@ -25,7 +25,7 @@ currently in active development, so this documentation will change frequently. For an overview of Docker, please see the `Introduction -`_. When you're ready to start working with +`_. When you're ready to start working with Docker, we have a `quick start `_ and a more in-depth guide to :ref:`ubuntu_linux` and other :ref:`installation_list` paths including prebuilt binaries, diff --git a/components/engine/docs/sources/installation/binaries.rst b/components/engine/docs/sources/installation/binaries.rst index 759f8a7502..f06a8d6c5f 100644 --- a/components/engine/docs/sources/installation/binaries.rst +++ b/components/engine/docs/sources/installation/binaries.rst @@ -21,6 +21,11 @@ Check Your Kernel Your host's Linux kernel must meet the Docker :ref:`kernel` +Check for User Space Tools +-------------------------- + +You must have a working installation of the `lxc `_ utilities and library. + Get the docker binary: ---------------------- diff --git a/components/engine/docs/sources/installation/fedora.rst b/components/engine/docs/sources/installation/fedora.rst index 9525371f03..de296b4df2 100644 --- a/components/engine/docs/sources/installation/fedora.rst +++ b/components/engine/docs/sources/installation/fedora.rst @@ -1,6 +1,6 @@ :title: Requirements and Installation on Fedora :description: Please note this project is currently under heavy development. It should not be used in production. -:keywords: Docker, Docker documentation, fedora, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux +:keywords: Docker, Docker documentation, Fedora, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux .. _fedora: @@ -18,13 +18,34 @@ architecture. Installation ------------ +The ``docker-io`` package provides Docker on Fedora. + + +If you have the (unrelated) ``docker`` package installed already, it will +conflict with ``docker-io``. There's a `bug report`_ filed for it. +To proceed with ``docker-io`` installation on Fedora 19, please remove +``docker`` first. + +.. code-block:: bash + + sudo yum -y remove docker + +For Fedora 20 and later, the ``wmdocker`` package will provide the same +functionality as ``docker`` and will also not conflict with ``docker-io``. + +.. code-block:: bash + + sudo yum -y install wmdocker + sudo yum -y remove docker + Install the ``docker-io`` package which will install Docker on our host. .. code-block:: bash sudo yum -y install docker-io -To update the ``docker-io`` package + +To update the ``docker-io`` package: .. code-block:: bash @@ -50,3 +71,5 @@ Now let's verify that Docker is working. **Done!**, now continue with the :ref:`hello_world` example. +.. _bug report: https://bugzilla.redhat.com/show_bug.cgi?id=1043676 + diff --git a/components/engine/docs/sources/installation/frugalware.rst b/components/engine/docs/sources/installation/frugalware.rst new file mode 100644 index 0000000000..cda6c4bfc4 --- /dev/null +++ b/components/engine/docs/sources/installation/frugalware.rst @@ -0,0 +1,80 @@ +:title: Installation on FrugalWare +:description: Docker installation on FrugalWare. +:keywords: frugalware linux, virtualization, docker, documentation, installation + +.. _frugalware: + +FrugalWare +========== + +.. include:: install_header.inc + +.. include:: install_unofficial.inc + +Installing on FrugalWare is handled via the official packages: + +* `lxc-docker i686 `_ + +* `lxc-docker x86_64 `_ + +The `lxc-docker` package will install the latest tagged version of Docker. + +Dependencies +------------ + +Docker depends on several packages which are specified as dependencies in +the packages. The core dependencies are: + +* systemd +* lvm2 +* sqlite3 +* libguestfs +* lxc +* iproute2 +* bridge-utils + + +Installation +------------ + +A simple +:: + + pacman -S lxc-docker + +is all that is needed. + + +Starting Docker +--------------- + +There is a systemd service unit created for Docker. To start Docker as service: + +:: + + sudo systemctl start lxc-docker + + +To start on system boot: + +:: + + sudo systemctl enable lxc-docker + +Network Configuration +--------------------- + +IPv4 packet forwarding is disabled by default on FrugalWare, so Internet access from inside +the container may not work. + +To enable packet forwarding, run the following command as the ``root`` user on the host system: + +:: + + sysctl net.ipv4.ip_forward=1 + +And, to make it persistent across reboots, add the following to a file named **/etc/sysctl.d/docker.conf**: + +:: + + net.ipv4.ip_forward=1 diff --git a/components/engine/docs/sources/installation/google.rst b/components/engine/docs/sources/installation/google.rst index e3286d1d90..ff38e1e6e4 100644 --- a/components/engine/docs/sources/installation/google.rst +++ b/components/engine/docs/sources/installation/google.rst @@ -57,9 +57,17 @@ docker-playground:~$ curl get.docker.io | bash docker-playground:~$ sudo update-rc.d docker defaults -7. Start a new container: +7. If running in zones: us-central1-a, europe-west1-1, and europe-west1-b, the docker daemon must be started with the `-mtu` flag. Without the flag, you may experience intermittent network pauses. +`See this issue `_ for more details. + +.. code-block:: bash + + docker -d -mtu 1460 + +8. Start a new container: .. code-block:: bash docker-playground:~$ sudo docker run busybox echo 'docker on GCE \o/' docker on GCE \o/ + diff --git a/components/engine/docs/sources/installation/index.rst b/components/engine/docs/sources/installation/index.rst index b2882a5cb3..9026b1f7f4 100644 --- a/components/engine/docs/sources/installation/index.rst +++ b/components/engine/docs/sources/installation/index.rst @@ -22,6 +22,7 @@ Contents: fedora archlinux gentoolinux + frugalware vagrant windows amazon diff --git a/components/engine/docs/sources/installation/kernel.rst b/components/engine/docs/sources/installation/kernel.rst index b2cf4f1479..8338cfdc88 100644 --- a/components/engine/docs/sources/installation/kernel.rst +++ b/components/engine/docs/sources/installation/kernel.rst @@ -115,6 +115,8 @@ Then run ``update-grub``, and reboot. Details ------- +To automatically check some of the requirements below, you can run `lxc-checkconfig`. + Networking: - CONFIG_BRIDGE diff --git a/components/engine/docs/sources/installation/rhel.rst b/components/engine/docs/sources/installation/rhel.rst index 993d329df7..b928b333f4 100644 --- a/components/engine/docs/sources/installation/rhel.rst +++ b/components/engine/docs/sources/installation/rhel.rst @@ -28,6 +28,15 @@ Installation Firstly, you need to install the EPEL repository. Please follow the `EPEL installation instructions`_. +The ``docker-io`` package provides Docker on EPEL. + + +If you already have the (unrelated) ``docker`` package installed, it will +conflict with ``docker-io``. There's a `bug report`_ filed for it. +To proceed with ``docker-io`` installation, please remove +``docker`` first. + + Next, let's install the ``docker-io`` package which will install Docker on our host. .. code-block:: bash @@ -68,4 +77,5 @@ If you have any issues - please report them directly in the `Red Hat Bugzilla fo .. _Extra Packages for Enterprise Linux (EPEL): https://fedoraproject.org/wiki/EPEL .. _EPEL installation instructions: https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F .. _Red Hat Bugzilla for docker-io component : https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora%20EPEL&component=docker-io +.. _bug report: https://bugzilla.redhat.com/show_bug.cgi?id=1043676 diff --git a/components/engine/docs/sources/use/basics.rst b/components/engine/docs/sources/use/basics.rst index 6b2c588817..8c896ae793 100644 --- a/components/engine/docs/sources/use/basics.rst +++ b/components/engine/docs/sources/use/basics.rst @@ -9,7 +9,7 @@ Learn Basic Commands Starting Docker --------------- -If you have used one of the quick install paths', Docker may have been +If you have used one of the quick install paths, Docker may have been installed with upstart, Ubuntu's system for starting processes at boot time. You should be able to run ``sudo docker help`` and get output. @@ -30,8 +30,8 @@ Download a pre-built image # Download an ubuntu image sudo docker pull ubuntu -This will find the ``ubuntu`` image by name in the :ref:`Central Index -` and download it from the top-level Central +This will find the ``ubuntu`` image by name in the :ref:`Central Index +` and download it from the top-level Central Repository to a local image cache. .. NOTE:: When the image has successfully downloaded, you will see a @@ -53,21 +53,23 @@ Running an interactive shell .. _dockergroup: -sudo and the docker Group -------------------------- +The sudo command and the docker Group +------------------------------------- -The ``docker`` daemon always runs as root, and since ``docker`` -version 0.5.2, ``docker`` binds to a Unix socket instead of a TCP -port. By default that Unix socket is owned by the user *root*, and so, -by default, you can access it with ``sudo``. +The ``docker`` daemon always runs as the root user, and since Docker version +0.5.2, the ``docker`` daemon binds to a Unix socket instead of a TCP port. By +default that Unix socket is owned by the user *root*, and so, by default, you +can access it with ``sudo``. Starting in version 0.5.3, if you (or your Docker installer) create a Unix group called *docker* and add users to it, then the ``docker`` daemon will make the ownership of the Unix socket read/writable by the *docker* group when the daemon starts. The ``docker`` daemon must -always run as root, but if you run the ``docker`` client as a user in +always run as the root user, but if you run the ``docker`` client as a user in the *docker* group then you don't need to add ``sudo`` to all the -client commands. Warning: the *docker* group is root-equivalent. +client commands. + +.. warning:: The *docker* group is root-equivalent. **Example:** @@ -97,10 +99,10 @@ Bind Docker to another host/port or a Unix socket `_). Make sure you control access to ``docker``. -With -H it is possible to make the Docker daemon to listen on a -specific ip and port. By default, it will listen on +With ``-H`` it is possible to make the Docker daemon to listen on a +specific IP and port. By default, it will listen on ``unix:///var/run/docker.sock`` to allow only local connections by the -*root* user. You *could* set it to 0.0.0.0:4243 or a specific host ip to +*root* user. You *could* set it to ``0.0.0.0:4243`` or a specific host IP to give access to everybody, but that is **not recommended** because then it is trivial for someone to gain root access to the host where the daemon is running. @@ -179,10 +181,10 @@ Committing (saving) a container state Save your containers state to a container image, so the state can be re-used. -When you commit your container only the differences between the image -the container was created from and the current state of the container -will be stored (as a diff). See which images you already have using -``sudo docker images`` +When you commit your container only the differences between the image the +container was created from and the current state of the container will be +stored (as a diff). See which images you already have using the ``docker +images`` command. .. code-block:: bash @@ -194,7 +196,5 @@ will be stored (as a diff). See which images you already have using 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` diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index 10140313a8..81145a6ee8 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -251,6 +251,11 @@ All new files and directories are created with mode 0755, uid and gid if you build using STDIN (``docker build - < somefile``), there is no build context, so the Dockerfile can only contain an URL based ADD statement. +.. note:: + if your URL files are protected using authentication, you will need to use + an ``RUN wget`` , ``RUN curl`` or other tool from within the container as + ADD does not support authentication. + The copy obeys the following rules: * The ```` path must be inside the *context* of the build; you cannot diff --git a/components/engine/docs/sources/use/port_redirection.rst b/components/engine/docs/sources/use/port_redirection.rst index b35d27a3db..5cddb238e4 100644 --- a/components/engine/docs/sources/use/port_redirection.rst +++ b/components/engine/docs/sources/use/port_redirection.rst @@ -31,7 +31,7 @@ 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 +Binding a port to a host interface ----------------------------------- To bind a port of the container to a specific interface of the host diff --git a/components/engine/docs/sources/use/working_with_volumes.rst b/components/engine/docs/sources/use/working_with_volumes.rst index 341bfe8d6a..86576b05e4 100644 --- a/components/engine/docs/sources/use/working_with_volumes.rst +++ b/components/engine/docs/sources/use/working_with_volumes.rst @@ -13,7 +13,7 @@ Share Directories via Volumes 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: +features for persistent 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 @@ -30,35 +30,58 @@ 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:: +Using data volumes is as simple as adding a ``-v`` parameter to the ``docker run`` +command. The ``-v`` parameter can be used more than once in order to +create more volumes within the new container. To create a new container with +two new volumes:: - docker run -v /var/volume1 -v /var/volume2 shykes/couchdb + $ docker run -v /var/volume1 -v /var/volume2 busybox true -For a Dockerfile, the VOLUME instruction will add one or more new -volumes to any container created from the image:: +This command will create the new container with two new volumes that +exits instantly (``true`` is pretty much the smallest, simplest program +that you can run). Once created you can mount its volumes in any other +container using the ``-volumes-from`` option; irrespecive of whether the +container is running or not. - VOLUME ["/var/volume1", "/var/volume2"] +Or, you can use the VOLUME instruction in a Dockerfile to add one or more new +volumes to any container created from that image:: + # BUILD-USING: docker build -t data . + # RUN-USING: docker run -name DATA data + FROM busybox + VOLUME ["/var/volume1", "/var/volume2"] + CMD ["/usr/bin/true"] -Mount Volumes from an Existing Container: ------------------------------------------ +Creating and mounting a Data Volume Container +--------------------------------------------- -The command below creates a new container which is running as daemon -``-d`` and with one volume ``/var/lib/couchdb``:: +If you have some persistent data that you want to share between containers, +or want to use from non-persistent containers, its best to create a named +Data Volume Container, and then to mount the data from it. - COUCH1=$(sudo docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03) +Create a named container with volumes to share (``/var/volume1`` and ``/var/volume2``):: -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``:: + $ docker run -v /var/volume1 -v /var/volume2 -name DATA busybox true - COUCH2=$(sudo docker run -d -volumes-from $COUCH1 shykes/couchdb:2013-05-03) +Then mount those data volumes into your application containers:: -Now, the second container has the all the information from the first volume. + $ docker run -t -i -rm -volumes-from DATA -name client1 ubuntu bash +You can use multiple ``-volumes-from`` parameters to bring together multiple +data volumes from multiple containers. + +Interestingly, you can mount the volumes that came from the ``DATA`` container in +yet another container via the ``client1`` middleman container:: + + $ docker run -t -i -rm -volumes-from client1 ubuntu -name client2 bash + +This allows you to abstract the actual data source from users of that data, +similar to :ref:`ambassador_pattern_linking `. + +If you remove containers that mount volumes, including the initial DATA container, +or the middleman, the volumes will not be deleted until there are no containers still +referencing those volumes. This allows you to upgrade, or effectivly migrate data volumes +between containers. Mount a Host Directory as a Container Volume: --------------------------------------------- @@ -68,13 +91,13 @@ Mount a Host Directory as a Container Volume: -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. +This is not available from a Dockerfile as it makes the built image less portable +or shareable. [host-dir] volumes are 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 + sudo docker run -v /var/logs:/var/host_logs:ro ubuntu bash The command above mounts the host directory ``/var/logs`` into the container with read only permissions as ``/var/host_logs``. @@ -87,3 +110,6 @@ Known Issues * :issue:`2702`: "lxc-start: Permission denied - failed to mount" could indicate a permissions problem with AppArmor. Please see the issue for a workaround. +* :issue:`2528`: the busybox container is used to make the resulting container as small and + simple as possible - whenever you need to interact with the data in the volume + you mount it into another container. diff --git a/components/engine/docs/theme/docker/layout.html b/components/engine/docs/theme/docker/layout.html index 67b3e49745..a966556044 100755 --- a/components/engine/docs/theme/docker/layout.html +++ b/components/engine/docs/theme/docker/layout.html @@ -86,26 +86,26 @@ -
+
-
+
-