Merge branch 'master' into add_freebsd_support
Conflicts: archive/archive.go archive/start_unsupported.go
This commit is contained in:
commit
bb43761940
311 changed files with 14052 additions and 4785 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -22,3 +22,4 @@ bundles/
|
|||
.git/
|
||||
vendor/pkg/
|
||||
pyenv
|
||||
Vagrantfile
|
||||
|
|
1
AUTHORS
1
AUTHORS
|
@ -334,6 +334,7 @@ Wes Morgan <cap10morgan@gmail.com>
|
|||
Will Dietz <w@wdtz.org>
|
||||
William Delanoue <william.delanoue@gmail.com>
|
||||
Will Rouesnel <w.rouesnel@gmail.com>
|
||||
Will Weaver <monkey@buildingbananas.com>
|
||||
Xiuming Chen <cc@cxm.cc>
|
||||
Yang Bai <hamo.by@gmail.com>
|
||||
Yurii Rashkovskii <yrashk@gmail.com>
|
||||
|
|
45
CHANGELOG.md
45
CHANGELOG.md
|
@ -1,5 +1,50 @@
|
|||
# Changelog
|
||||
|
||||
## 0.8.1 (2014-02-18)
|
||||
|
||||
#### Builder
|
||||
|
||||
- Avoid extra mount/unmount during build. This removes an unneeded mount/unmount operation which was causing problems with devicemapper
|
||||
- Fix regression with ADD of tar files. This stops Docker from decompressing tarballs added via ADD from the local file system
|
||||
- Add error to `docker build --rm`. This adds a missing error check to ensure failures to remove containers are detected and reported
|
||||
|
||||
#### Documentation
|
||||
|
||||
* Update issue filing instructions
|
||||
* Warn against the use of symlinks for Docker's storage folder
|
||||
* Replace the Firefox example with an IceWeasel example
|
||||
* Rewrite the PostgresSQL example using a Dockerfile and add more details to it
|
||||
* Improve the OS X documentation
|
||||
|
||||
#### Remote API
|
||||
|
||||
- Fix broken images API for version less than 1.7
|
||||
- Use the right encoding for all API endpoints which return JSON
|
||||
- Move remote api client to api/
|
||||
- Queue calls to the API using generic socket wait
|
||||
|
||||
#### Runtime
|
||||
|
||||
- Fix the use of custom settings for bridges and custom bridges
|
||||
- Refactor the devicemapper code to avoid many mount/unmount race conditions and failures
|
||||
- Remove two panics which could make Docker crash in some situations
|
||||
- Don't ping registry from the CLI client
|
||||
- Enable skip_block_zeroing for devicemapper. This stops devicemapper from always zeroing entire blocks
|
||||
- Fix --run in `docker commit`. This makes docker commit store `--run` in the image configuration
|
||||
- Remove directory when removing devicemapper device. This cleans up leftover mount directories
|
||||
- Drop NET_ADMIN capability for non-privileged containers. Unprivileged containers can't change their network configuration
|
||||
- Ensure `docker cp` stream is closed properly
|
||||
- Avoid extra mount/unmount during container registration. This removes an unneeded mount/unmount operation which was causing problems with devicemapper
|
||||
- Stop allowing tcp:// as a default tcp bin address which binds to 127.0.0.1:4243 and remove the default port
|
||||
+ Mount-bind the PTY as container console. This allows tmux and screen to run in a container
|
||||
- Clean up archive closing. This fixes and improves archive handling
|
||||
- Fix engine tests on systems where temp directories are symlinked
|
||||
- Add test methods for save and load
|
||||
- Avoid temporarily unmounting the container when restarting it. This fixes a race for devicemapper during restart
|
||||
- Support submodules when building from a GitHub repository
|
||||
- Quote volume path to allow spaces
|
||||
- Fix remote tar ADD behavior. This fixes a regression which was causing Docker to extract tarballs
|
||||
|
||||
## 0.8.0 (2014-02-04)
|
||||
|
||||
#### Notable features since 0.7.0
|
||||
|
|
|
@ -88,7 +88,7 @@ curl -o .git/hooks/pre-commit https://raw.github.com/edsrzf/gofmt-git-hook/maste
|
|||
Pull requests descriptions should be as clear as possible and include a
|
||||
reference to all the issues that they address.
|
||||
|
||||
Pull requests mustn't contain commits from other users or branches.
|
||||
Pull requests must not contain commits from other users or branches.
|
||||
|
||||
Code review comments may be added to your pull request. Discuss, then make the
|
||||
suggested modifications and push additional commits to your feature branch. Be
|
||||
|
@ -109,6 +109,18 @@ name and email address match your git configuration. The AUTHORS file is
|
|||
regenerated occasionally from the git commit history, so a mismatch may result
|
||||
in your changes being overwritten.
|
||||
|
||||
### Merge approval
|
||||
|
||||
Docker maintainers use LGTM (looks good to me) in comments on the code review
|
||||
to indicate acceptance.
|
||||
|
||||
A change requires LGTMs from an absolute majority of the maintainers of each
|
||||
component affected. For example, if a change affects docs/ and registry/, it
|
||||
needs an absolute majority from the maintainers of docs/ AND, separately, an
|
||||
absolute majority of the maintainers of registry.
|
||||
|
||||
For more details see [MAINTAINERS.md](hack/MAINTAINERS.md)
|
||||
|
||||
### Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the
|
||||
|
@ -117,7 +129,7 @@ pass it on as an open-source patch. The rules are pretty simple: if you
|
|||
can certify the below:
|
||||
|
||||
```
|
||||
Docker Developer Grant and Certificate of Origin 1.1
|
||||
Docker Developer Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to the Docker Project ("Project"), I represent and
|
||||
warrant that:
|
||||
|
@ -158,16 +170,21 @@ curl -o .git/hooks/prepare-commit-msg https://raw.github.com/dotcloud/docker/mas
|
|||
|
||||
* Note: the above script expects to find your GitHub user name in ``git config --get github.user``
|
||||
|
||||
#### Small patch exception
|
||||
|
||||
There are several exceptions to the signing requirement. Currently these are:
|
||||
|
||||
* Your patch fixes spelling or grammar errors.
|
||||
* Your patch is a single line change to documentation.
|
||||
|
||||
If you have any questions, please refer to the FAQ in the [docs](http://docs.docker.io)
|
||||
|
||||
|
||||
|
||||
### How can I become a maintainer?
|
||||
|
||||
* Step 1: learn the component inside out
|
||||
* Step 2: make yourself useful by contributing code, bugfixes, support etc.
|
||||
* Step 3: volunteer on the irc channel (#docker@freenode)
|
||||
* Step 4: propose yourself at a scheduled #docker-meeting
|
||||
* Step 4: propose yourself at a scheduled docker meeting in #docker-dev
|
||||
|
||||
Don't forget: being a maintainer is a time investment. Make sure you will have time to make yourself available.
|
||||
You don't have to be a maintainer to make a difference on the project!
|
||||
|
|
|
@ -62,7 +62,7 @@ RUN cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper
|
|||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# Install Go
|
||||
RUN curl -s https://go.googlecode.com/files/go1.2.src.tar.gz | tar -v -C /usr/local -xz
|
||||
RUN curl -s https://go.googlecode.com/files/go1.2.1.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
|
||||
|
@ -87,6 +87,7 @@ RUN git config --global user.email 'docker-dummy@example.com'
|
|||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/dotcloud/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
|
6
FIXME
6
FIXME
|
@ -11,20 +11,14 @@ They are just like FIXME comments in the source code, except we're not sure wher
|
|||
to put them - so we put them here :)
|
||||
|
||||
|
||||
* Merge Runtime, Server and Builder into Runtime
|
||||
* Run linter on codebase
|
||||
* Unify build commands and regular commands
|
||||
* Move source code into src/ subdir for clarity
|
||||
* docker build: on non-existent local path for ADD, don't show full absolute path on the host
|
||||
* docker tag foo REPO:TAG
|
||||
* use size header for progress bar in pull
|
||||
* Clean up context upload in build!!!
|
||||
* Parallel pull
|
||||
* Always generate a resolv.conf per container, to avoid changing resolv.conf under thne container's feet
|
||||
* Save metadata with import/export (#1974)
|
||||
* Upgrade dockerd without stopping containers
|
||||
* Simple command to remove all untagged images (`docker rmi $(docker images | awk '/^<none>/ { print $3 }')`)
|
||||
* Simple command to clean up containers for disk space
|
||||
* Caching after an ADD (#880)
|
||||
* Clean up the ProgressReader api, it's a PITA to use
|
||||
* Use netlink instead of iproute2/iptables (#925)
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
Solomon Hykes <solomon@dotcloud.com> (@shykes)
|
||||
Guillaume Charmes <guillaume@dotcloud.com> (@creack)
|
||||
Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Victor Vieux <vieux@docker.com> (@vieux)
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
.travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
api.go: Victor Vieux <victor@dotcloud.com> (@vieux)
|
||||
Dockerfile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Makefile: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Vagrantfile: Cristian Staretu <cristian.staretu@gmail.com> (@unclejack)
|
||||
|
|
10
Makefile
10
Makefile
|
@ -3,7 +3,7 @@
|
|||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
|
||||
DOCKER_IMAGE := docker:$(GIT_BRANCH)
|
||||
DOCKER_DOCS_IMAGE := docker-docs:$(GIT_BRANCH)
|
||||
DOCKER_RUN_DOCKER := docker run -rm -i -t -privileged -e TESTFLAGS -v $(CURDIR)/bundles:/go/src/github.com/dotcloud/docker/bundles "$(DOCKER_IMAGE)"
|
||||
DOCKER_RUN_DOCKER := docker run --rm -i -t --privileged -e TESTFLAGS -v "$(CURDIR)/bundles:/go/src/github.com/dotcloud/docker/bundles" "$(DOCKER_IMAGE)"
|
||||
|
||||
default: binary
|
||||
|
||||
|
@ -17,10 +17,10 @@ cross: build
|
|||
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross
|
||||
|
||||
docs: docs-build
|
||||
docker run -rm -i -t -p 8000:8000 "$(DOCKER_DOCS_IMAGE)"
|
||||
docker run --rm -i -t -p 8000:8000 "$(DOCKER_DOCS_IMAGE)"
|
||||
|
||||
docs-shell: docs-build
|
||||
docker run -rm -i -t -p 8000:8000 "$(DOCKER_DOCS_IMAGE)" bash
|
||||
docker run --rm -i -t -p 8000:8000 "$(DOCKER_DOCS_IMAGE)" bash
|
||||
|
||||
test: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test test-integration
|
||||
|
@ -32,10 +32,10 @@ shell: build
|
|||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
build: bundles
|
||||
docker build -rm -t "$(DOCKER_IMAGE)" .
|
||||
docker build -t "$(DOCKER_IMAGE)" .
|
||||
|
||||
docs-build:
|
||||
docker build -rm -t "$(DOCKER_DOCS_IMAGE)" docs
|
||||
docker build -t "$(DOCKER_DOCS_IMAGE)" docs
|
||||
|
||||
bundles:
|
||||
mkdir bundles
|
||||
|
|
107
README.md
107
README.md
|
@ -4,19 +4,19 @@ Docker: the Linux container engine
|
|||
Docker is an open source project to pack, ship and run any application
|
||||
as a lightweight container
|
||||
|
||||
Docker containers are both *hardware-agnostic* and
|
||||
*platform-agnostic*. This means that they can run anywhere, from your
|
||||
laptop to the largest EC2 compute instance and everything in between -
|
||||
and they don't require that you use a particular language, framework
|
||||
or packaging system. That makes them great building blocks for
|
||||
deploying and scaling web apps, databases and backend services without
|
||||
depending on a particular stack or provider.
|
||||
Docker containers are both *hardware-agnostic* and *platform-agnostic*.
|
||||
This means that they can run anywhere, from your laptop to the largest
|
||||
EC2 compute instance and everything in between - and they don't require
|
||||
that you use a particular language, framework or packaging system. That
|
||||
makes them great building blocks for deploying and scaling web apps,
|
||||
databases and backend services without depending on a particular stack
|
||||
or provider.
|
||||
|
||||
Docker is an open-source implementation of the deployment engine which
|
||||
powers [dotCloud](http://dotcloud.com), a popular
|
||||
Platform-as-a-Service. It benefits directly from the experience
|
||||
accumulated over several years of large-scale operation and support of
|
||||
hundreds of thousands of applications and databases.
|
||||
powers [dotCloud](http://dotcloud.com), a popular Platform-as-a-Service.
|
||||
It benefits directly from the experience accumulated over several years
|
||||
of large-scale operation and support of hundreds of thousands of
|
||||
applications and databases.
|
||||
|
||||

|
||||
|
||||
|
@ -24,10 +24,10 @@ hundreds of thousands of applications and databases.
|
|||
|
||||
A common method for distributing applications and sandboxing their
|
||||
execution is to use virtual machines, or VMs. Typical VM formats are
|
||||
VMWare's vmdk, Oracle Virtualbox's vdi, and Amazon EC2's ami. In
|
||||
theory these formats should allow every developer to automatically
|
||||
package their application into a "machine" for easy distribution and
|
||||
deployment. In practice, that almost never happens, for a few reasons:
|
||||
VMWare's vmdk, Oracle Virtualbox's vdi, and Amazon EC2's ami. In theory
|
||||
these formats should allow every developer to automatically package
|
||||
their application into a "machine" for easy distribution and deployment.
|
||||
In practice, that almost never happens, for a few reasons:
|
||||
|
||||
* *Size*: VMs are very large which makes them impractical to store
|
||||
and transfer.
|
||||
|
@ -47,39 +47,37 @@ deployment. In practice, that almost never happens, for a few reasons:
|
|||
service discovery.
|
||||
|
||||
By contrast, Docker relies on a different sandboxing method known as
|
||||
*containerization*. Unlike traditional virtualization,
|
||||
containerization takes place at the kernel level. Most modern
|
||||
operating system kernels now support the primitives necessary for
|
||||
containerization, including Linux with [openvz](http://openvz.org),
|
||||
*containerization*. Unlike traditional virtualization, containerization
|
||||
takes place at the kernel level. Most modern operating system kernels
|
||||
now support the primitives necessary for containerization, including
|
||||
Linux with [openvz](http://openvz.org),
|
||||
[vserver](http://linux-vserver.org) and more recently
|
||||
[lxc](http://lxc.sourceforge.net), Solaris with
|
||||
[zones](http://docs.oracle.com/cd/E26502_01/html/E29024/preface-1.html#scrolltoc)
|
||||
and FreeBSD with
|
||||
[Jails](http://www.freebsd.org/doc/handbook/jails.html).
|
||||
|
||||
Docker builds on top of these low-level primitives to offer developers
|
||||
a portable format and runtime environment that solves all 4
|
||||
problems. Docker containers are small (and their transfer can be
|
||||
optimized with layers), they have basically zero memory and cpu
|
||||
overhead, they are completely portable and are designed from the
|
||||
ground up with an application-centric design.
|
||||
Docker builds on top of these low-level primitives to offer developers a
|
||||
portable format and runtime environment that solves all 4 problems.
|
||||
Docker containers are small (and their transfer can be optimized with
|
||||
layers), they have basically zero memory and cpu overhead, they are
|
||||
completely portable and are designed from the ground up with an
|
||||
application-centric design.
|
||||
|
||||
The best part: because ``docker`` operates at the OS level, it can
|
||||
still be run inside a VM!
|
||||
The best part: because Docker operates at the OS level, it can still be
|
||||
run inside a VM!
|
||||
|
||||
## Plays well with others
|
||||
|
||||
Docker does not require that you buy into a particular programming
|
||||
language, framework, packaging system or configuration language.
|
||||
|
||||
Is your application a Unix process? Does it use files, tcp
|
||||
connections, environment variables, standard Unix streams and
|
||||
command-line arguments as inputs and outputs? Then ``docker`` can run
|
||||
it.
|
||||
Is your application a Unix process? Does it use files, tcp connections,
|
||||
environment variables, standard Unix streams and command-line arguments
|
||||
as inputs and outputs? Then Docker can run it.
|
||||
|
||||
Can your application's build be expressed as a sequence of such
|
||||
commands? Then ``docker`` can build it.
|
||||
|
||||
commands? Then Docker can build it.
|
||||
|
||||
## Escape dependency hell
|
||||
|
||||
|
@ -126,14 +124,11 @@ build command inherits the result of the previous commands, the
|
|||
Here's a typical Docker build process:
|
||||
|
||||
```bash
|
||||
from ubuntu:12.10
|
||||
run apt-get update
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -q -y python
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -q -y python-pip
|
||||
run pip install django
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -q -y curl
|
||||
run curl -L https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xzv
|
||||
run cd helloflask-master && pip install -r requirements.txt
|
||||
FROM ubuntu:12.04
|
||||
RUN apt-get update
|
||||
RUN apt-get install -q -y python python-pip curl
|
||||
RUN curl -L https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xzv
|
||||
RUN cd helloflask-master && pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Note that Docker doesn't care *how* dependencies are built - as long
|
||||
|
@ -143,22 +138,25 @@ as they can be built by running a Unix command in a container.
|
|||
Getting started
|
||||
===============
|
||||
|
||||
Docker can be installed on your local machine as well as servers - both bare metal and virtualized.
|
||||
It is available as a binary on most modern Linux systems, or as a VM on Windows, Mac and other systems.
|
||||
Docker can be installed on your local machine as well as servers - both
|
||||
bare metal and virtualized. It is available as a binary on most modern
|
||||
Linux systems, or as a VM on Windows, Mac and other systems.
|
||||
|
||||
We also offer an interactive tutorial for quickly learning the basics of using Docker.
|
||||
|
||||
|
||||
For up-to-date install instructions and online tutorials, see the [Getting Started page](http://www.docker.io/gettingstarted/).
|
||||
We also offer an interactive tutorial for quickly learning the basics of
|
||||
using Docker.
|
||||
|
||||
For up-to-date install instructions and online tutorials, see the
|
||||
[Getting Started page](http://www.docker.io/gettingstarted/).
|
||||
|
||||
Usage examples
|
||||
==============
|
||||
|
||||
Docker can be used to run short-lived commands, long-running daemons (app servers, databases etc.),
|
||||
interactive shell sessions, etc.
|
||||
Docker can be used to run short-lived commands, long-running daemons
|
||||
(app servers, databases etc.), interactive shell sessions, etc.
|
||||
|
||||
You can find a [list of real-world examples](http://docs.docker.io/en/latest/examples/) in the documentation.
|
||||
You can find a [list of real-world
|
||||
examples](http://docs.docker.io/en/latest/examples/) in the
|
||||
documentation.
|
||||
|
||||
Under the hood
|
||||
--------------
|
||||
|
@ -170,13 +168,7 @@ Under the hood, Docker is built on the following components:
|
|||
and
|
||||
[namespacing](http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part)
|
||||
capabilities of the Linux kernel;
|
||||
* [AUFS](http://aufs.sourceforge.net/aufs.html), a powerful union
|
||||
filesystem with copy-on-write capabilities;
|
||||
* The [Go](http://golang.org) programming language;
|
||||
* [lxc](http://lxc.sourceforge.net/), a set of convenience scripts to
|
||||
simplify the creation of Linux containers.
|
||||
|
||||
|
||||
* The [Go](http://golang.org) programming language.
|
||||
|
||||
Contributing to Docker
|
||||
======================
|
||||
|
@ -187,7 +179,6 @@ started [here](CONTRIBUTING.md).
|
|||
They are probably not perfect, please let us know if anything feels
|
||||
wrong or incomplete.
|
||||
|
||||
|
||||
### Legal
|
||||
|
||||
*Brought to you courtesy of our legal counsel. For more context,
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.8.0-dev
|
||||
0.8.1-dev
|
||||
|
|
206
Vagrantfile
vendored
206
Vagrantfile
vendored
|
@ -1,206 +0,0 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
|
||||
BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
|
||||
VF_BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64_vmware_fusion.box"
|
||||
AWS_BOX_URI = ENV['BOX_URI'] || "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
|
||||
AWS_REGION = ENV['AWS_REGION'] || "us-east-1"
|
||||
AWS_AMI = ENV['AWS_AMI'] || "ami-69f5a900"
|
||||
AWS_INSTANCE_TYPE = ENV['AWS_INSTANCE_TYPE'] || 't1.micro'
|
||||
SSH_PRIVKEY_PATH = ENV['SSH_PRIVKEY_PATH']
|
||||
PRIVATE_NETWORK = ENV['PRIVATE_NETWORK']
|
||||
|
||||
# Boolean that forwards the Docker dynamic ports 49000-49900
|
||||
# See http://docs.docker.io/en/latest/use/port_redirection/ for more
|
||||
# $ FORWARD_DOCKER_PORTS=1 vagrant [up|reload]
|
||||
FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS']
|
||||
VAGRANT_RAM = ENV['VAGRANT_RAM'] || 512
|
||||
VAGRANT_CORES = ENV['VAGRANT_CORES'] || 1
|
||||
|
||||
# You may also provide a comma-separated list of ports
|
||||
# for Vagrant to forward. For example:
|
||||
# $ FORWARD_PORTS=8080,27017 vagrant [up|reload]
|
||||
FORWARD_PORTS = ENV['FORWARD_PORTS']
|
||||
|
||||
# A script to upgrade from the 12.04 kernel to the raring backport kernel (3.8)
|
||||
# and install docker.
|
||||
$script = <<SCRIPT
|
||||
# The username to add to the docker group will be passed as the first argument
|
||||
# to the script. If nothing is passed, default to "vagrant".
|
||||
user="$1"
|
||||
if [ -z "$user" ]; then
|
||||
user=vagrant
|
||||
fi
|
||||
|
||||
# Enable memory cgroup and swap accounting
|
||||
sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"/g' /etc/default/grub
|
||||
update-grub
|
||||
|
||||
# Adding an apt gpg key is idempotent.
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
|
||||
|
||||
# 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' > \
|
||||
/etc/apt/sources.list.d/docker.list
|
||||
|
||||
# Update remote package metadata. 'apt-get update' is idempotent.
|
||||
apt-get update -q
|
||||
|
||||
# Install docker. 'apt-get install' is idempotent.
|
||||
apt-get install -q -y lxc-docker
|
||||
|
||||
usermod -a -G docker "$user"
|
||||
|
||||
tmp=`mktemp -q` && {
|
||||
# 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.
|
||||
apt-get install -q -y --no-upgrade linux-image-generic-lts-raring | \
|
||||
tee "$tmp"
|
||||
|
||||
# Parse the number of installed packages from the output
|
||||
NUM_INST=`awk '$2 == "upgraded," && $4 == "newly" { print $3 }' "$tmp"`
|
||||
rm "$tmp"
|
||||
}
|
||||
|
||||
# If the number of installed packages is greater than 0, we want to reboot (the
|
||||
# backport kernel was installed but is not running).
|
||||
if [ "$NUM_INST" -gt 0 ];
|
||||
then
|
||||
echo "Rebooting down to activate new kernel."
|
||||
echo "/vagrant will not be mounted. Use 'vagrant halt' followed by"
|
||||
echo "'vagrant up' to ensure /vagrant is mounted."
|
||||
shutdown -r now
|
||||
fi
|
||||
SCRIPT
|
||||
|
||||
# We need to install the virtualbox guest additions *before* we do the normal
|
||||
# docker installation. As such this script is prepended to the common docker
|
||||
# install script above. This allows the install of the backport kernel to
|
||||
# trigger dkms to build the virtualbox guest module install.
|
||||
$vbox_script = <<VBOX_SCRIPT + $script
|
||||
# Install the VirtualBox guest additions if they aren't already installed.
|
||||
if [ ! -d /opt/VBoxGuestAdditions-4.3.6/ ]; then
|
||||
# Update remote package metadata. 'apt-get update' is idempotent.
|
||||
apt-get update -q
|
||||
|
||||
# Kernel Headers and dkms are required to build the vbox guest kernel
|
||||
# modules.
|
||||
apt-get install -q -y linux-headers-generic-lts-raring dkms
|
||||
|
||||
echo 'Downloading VBox Guest Additions...'
|
||||
wget -cq http://dlc.sun.com.edgesuite.net/virtualbox/4.3.6/VBoxGuestAdditions_4.3.6.iso
|
||||
echo "95648fcdb5d028e64145a2fe2f2f28c946d219da366389295a61fed296ca79f0 VBoxGuestAdditions_4.3.6.iso" | sha256sum --check || exit 1
|
||||
|
||||
mount -o loop,ro /home/vagrant/VBoxGuestAdditions_4.3.6.iso /mnt
|
||||
/mnt/VBoxLinuxAdditions.run --nox11
|
||||
umount /mnt
|
||||
fi
|
||||
VBOX_SCRIPT
|
||||
|
||||
Vagrant::Config.run do |config|
|
||||
# Setup virtual machine box. This VM configuration code is always executed.
|
||||
config.vm.box = BOX_NAME
|
||||
config.vm.box_url = BOX_URI
|
||||
|
||||
# Use the specified private key path if it is specified and not empty.
|
||||
if SSH_PRIVKEY_PATH
|
||||
config.ssh.private_key_path = SSH_PRIVKEY_PATH
|
||||
end
|
||||
|
||||
config.ssh.forward_agent = true
|
||||
end
|
||||
|
||||
# Providers were added on Vagrant >= 1.1.0
|
||||
#
|
||||
# NOTE: The vagrant "vm.provision" appends its arguments to a list and executes
|
||||
# them in order. If you invoke "vm.provision :shell, :inline => $script"
|
||||
# twice then vagrant will run the script two times. Unfortunately when you use
|
||||
# providers and the override argument to set up provisioners (like the vbox
|
||||
# guest extensions) they 1) don't replace the other provisioners (they append
|
||||
# to the end of the list) and 2) you can't control the order the provisioners
|
||||
# are executed (you can only append to the list). If you want the virtualbox
|
||||
# only script to run before the other script, you have to jump through a lot of
|
||||
# hoops.
|
||||
#
|
||||
# Here is my only repeatable solution: make one script that is common ($script)
|
||||
# and another script that is the virtual box guest *prepended* to the common
|
||||
# script. Only ever use "vm.provision" *one time* per provider. That means
|
||||
# every single provider has an override, and every single one configures
|
||||
# "vm.provision". Much saddness, but such is life.
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
config.vm.provider :aws do |aws, override|
|
||||
username = "ubuntu"
|
||||
override.vm.box_url = AWS_BOX_URI
|
||||
override.vm.provision :shell, :inline => $script, :args => username
|
||||
aws.access_key_id = ENV["AWS_ACCESS_KEY"]
|
||||
aws.secret_access_key = ENV["AWS_SECRET_KEY"]
|
||||
aws.keypair_name = ENV["AWS_KEYPAIR_NAME"]
|
||||
override.ssh.username = username
|
||||
aws.region = AWS_REGION
|
||||
aws.ami = AWS_AMI
|
||||
aws.instance_type = AWS_INSTANCE_TYPE
|
||||
end
|
||||
|
||||
config.vm.provider :rackspace do |rs, override|
|
||||
override.vm.provision :shell, :inline => $script
|
||||
rs.username = ENV["RS_USERNAME"]
|
||||
rs.api_key = ENV["RS_API_KEY"]
|
||||
rs.public_key_path = ENV["RS_PUBLIC_KEY"]
|
||||
rs.flavor = /512MB/
|
||||
rs.image = /Ubuntu/
|
||||
end
|
||||
|
||||
config.vm.provider :vmware_fusion do |f, override|
|
||||
override.vm.box_url = VF_BOX_URI
|
||||
override.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
override.vm.provision :shell, :inline => $script
|
||||
f.vmx["displayName"] = "docker"
|
||||
end
|
||||
|
||||
config.vm.provider :virtualbox do |vb, override|
|
||||
override.vm.provision :shell, :inline => $vbox_script
|
||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
|
||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
|
||||
vb.customize ["modifyvm", :id, "--memory", VAGRANT_RAM]
|
||||
vb.customize ["modifyvm", :id, "--cpus", VAGRANT_CORES]
|
||||
end
|
||||
end
|
||||
|
||||
# If this is a version 1 config, virtualbox is the only option. A version 2
|
||||
# config would have already been set in the above provider section.
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
config.vm.provision :shell, :inline => $vbox_script
|
||||
end
|
||||
|
||||
# Setup port forwarding per loaded environment variables
|
||||
forward_ports = FORWARD_DOCKER_PORTS.nil? ? [] : [*49153..49900]
|
||||
forward_ports += FORWARD_PORTS.split(',').map{|i| i.to_i } if FORWARD_PORTS
|
||||
if forward_ports.any?
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
forward_ports.each do |port|
|
||||
config.vm.forward_port port, port
|
||||
end
|
||||
end
|
||||
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
forward_ports.each do |port|
|
||||
config.vm.network :forwarded_port, :host => port, :guest => port, auto_correct: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !PRIVATE_NETWORK.nil?
|
||||
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
|
||||
config.vm.network :hostonly, PRIVATE_NETWORK
|
||||
end
|
||||
|
||||
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
|
||||
config.vm.network "private_network", ip: PRIVATE_NETWORK
|
||||
end
|
||||
end
|
||||
|
1
api/MAINTAINERS
Normal file
1
api/MAINTAINERS
Normal file
|
@ -0,0 +1 @@
|
|||
Victor Vieux <vieux@docker.com> (@vieux)
|
|
@ -1,21 +1,21 @@
|
|||
package docker
|
||||
package api
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/dockerversion"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/nat"
|
||||
flag "github.com/dotcloud/docker/pkg/mflag"
|
||||
"github.com/dotcloud/docker/pkg/sysinfo"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -29,7 +29,6 @@ import (
|
|||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
@ -38,13 +37,15 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
GITCOMMIT string
|
||||
VERSION string
|
||||
)
|
||||
var funcMap = template.FuncMap{
|
||||
"json": func(v interface{}) string {
|
||||
a, _ := json.Marshal(v)
|
||||
return string(a)
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
ErrConnectionRefused = errors.New("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
ErrConnectionRefused = errors.New("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
)
|
||||
|
||||
func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) {
|
||||
|
@ -80,7 +81,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", api.DEFAULTUNIXSOCKET)
|
||||
help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[unix://%s]: tcp://host:port to bind/connect to or unix://path/to/socket to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTUNIXSOCKET)
|
||||
for _, command := range [][]string{
|
||||
{"attach", "Attach to a running container"},
|
||||
{"build", "Build a container from a Dockerfile"},
|
||||
|
@ -139,37 +140,12 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
|
|||
return cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out, nil)
|
||||
}
|
||||
|
||||
// mkBuildContext returns an archive of an empty context with the contents
|
||||
// of `dockerfile` at the path ./Dockerfile
|
||||
func MkBuildContext(dockerfile string, files [][2]string) (archive.Archive, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
files = append(files, [2]string{"Dockerfile", dockerfile})
|
||||
for _, file := range files {
|
||||
name, content := file[0], file[1]
|
||||
hdr := &tar.Header{
|
||||
Name: name,
|
||||
Size: int64(len(content)),
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := tw.Write([]byte(content)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := tw.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
cmd := cli.Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH")
|
||||
tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success")
|
||||
suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress verbose build output")
|
||||
suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
|
||||
noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
|
||||
rm := cmd.Bool([]string{"#rm", "-rm"}, false, "Remove intermediate containers after a successful build")
|
||||
rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -191,7 +167,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
context, err = MkBuildContext(string(dockerfile), nil)
|
||||
context, err = archive.Generate("Dockerfile", string(dockerfile))
|
||||
} else if utils.IsURL(cmd.Arg(0)) || utils.IsGIT(cmd.Arg(0)) {
|
||||
isRemote = true
|
||||
} else {
|
||||
|
@ -209,7 +185,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|||
// FIXME: ProgressReader shouldn't be this annoying to use
|
||||
if context != nil {
|
||||
sf := utils.NewStreamFormatter(false)
|
||||
body = utils.ProgressReader(ioutil.NopCloser(context), 0, cli.err, sf, true, "", "Uploading context")
|
||||
body = utils.ProgressReader(context, 0, cli.err, sf, true, "", "Uploading context")
|
||||
}
|
||||
// Upload the build context
|
||||
v := &url.Values{}
|
||||
|
@ -257,9 +233,9 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
|||
|
||||
var username, password, email string
|
||||
|
||||
cmd.StringVar(&username, []string{"u", "-username"}, "", "username")
|
||||
cmd.StringVar(&password, []string{"p", "-password"}, "", "password")
|
||||
cmd.StringVar(&email, []string{"e", "-email"}, "", "email")
|
||||
cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
|
||||
cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
|
||||
cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
|
||||
err := cmd.Parse(args)
|
||||
if err != nil {
|
||||
return nil
|
||||
|
@ -388,12 +364,12 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
|||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
if VERSION != "" {
|
||||
fmt.Fprintf(cli.out, "Client version: %s\n", VERSION)
|
||||
if dockerversion.VERSION != "" {
|
||||
fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
|
||||
if GITCOMMIT != "" {
|
||||
fmt.Fprintf(cli.out, "Git commit (client): %s\n", GITCOMMIT)
|
||||
if dockerversion.GITCOMMIT != "" {
|
||||
fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
|
||||
}
|
||||
|
||||
body, _, err := readBody(cli.call("GET", "/version", nil, false))
|
||||
|
@ -418,7 +394,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
|||
release := utils.GetReleaseVersion()
|
||||
if release != "" {
|
||||
fmt.Fprintf(cli.out, "Last stable version: %s", release)
|
||||
if (VERSION != "" || remoteVersion.Exists("Version")) && (strings.Trim(VERSION, "-dev") != release || strings.Trim(remoteVersion.Get("Version"), "-dev") != release) {
|
||||
if (dockerversion.VERSION != "" || remoteVersion.Exists("Version")) && (strings.Trim(dockerversion.VERSION, "-dev") != release || strings.Trim(remoteVersion.Get("Version"), "-dev") != release) {
|
||||
fmt.Fprintf(cli.out, ", please update docker")
|
||||
}
|
||||
fmt.Fprintf(cli.out, "\n")
|
||||
|
@ -547,7 +523,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
|
|||
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
|
||||
encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
@ -587,7 +563,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
var tty bool
|
||||
if *attach || *openStdin {
|
||||
if cmd.NArg() > 1 {
|
||||
return fmt.Errorf("Impossible to start and attach multiple containers at once.")
|
||||
return fmt.Errorf("You cannot start and attach multiple containers at once.")
|
||||
}
|
||||
|
||||
body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false))
|
||||
|
@ -671,7 +647,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
|||
var tmpl *template.Template
|
||||
if *tmplStr != "" {
|
||||
var err error
|
||||
if tmpl, err = template.New("").Parse(*tmplStr); err != nil {
|
||||
if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
|
||||
fmt.Fprintf(cli.err, "Template parsing error: %v\n", err)
|
||||
return &utils.StatusError{StatusCode: 64,
|
||||
Status: "Template parsing error: " + err.Error()}
|
||||
|
@ -799,7 +775,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if frontends, exists := out.NetworkSettings.Ports[Port(port+"/"+proto)]; exists && frontends != nil {
|
||||
if frontends, exists := out.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
|
||||
for _, frontend := range frontends {
|
||||
fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort)
|
||||
}
|
||||
|
@ -811,7 +787,10 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
|||
|
||||
// 'docker rmi IMAGE' removes all images with the name IMAGE
|
||||
func (cli *DockerCli) CmdRmi(args ...string) error {
|
||||
cmd := cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
|
||||
var (
|
||||
cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
|
||||
force = cmd.Bool([]string{"f", "-force"}, false, "Force")
|
||||
)
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -820,9 +799,14 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
if *force {
|
||||
v.Set("force", "1")
|
||||
}
|
||||
|
||||
var encounteredError error
|
||||
for _, name := range cmd.Args() {
|
||||
body, _, err := readBody(cli.call("DELETE", "/images/"+name, nil, false))
|
||||
body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
encounteredError = fmt.Errorf("Error: failed to remove one or more images")
|
||||
|
@ -847,7 +831,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
|||
|
||||
func (cli *DockerCli) CmdHistory(args ...string) error {
|
||||
cmd := cli.Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "only show numeric IDs")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
|
||||
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
|
||||
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
|
@ -906,6 +890,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
|||
cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
|
||||
v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated to the container")
|
||||
link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
|
||||
force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
|
||||
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
|
@ -921,6 +906,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
|||
if *link {
|
||||
val.Set("link", "1")
|
||||
}
|
||||
if *force {
|
||||
val.Set("force", "1")
|
||||
}
|
||||
|
||||
var encounteredError error
|
||||
for _, name := range cmd.Args() {
|
||||
|
@ -1008,13 +996,13 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|||
|
||||
cli.LoadConfigFile()
|
||||
|
||||
// Resolve the Repository name from fqn to endpoint + name
|
||||
endpoint, _, err := registry.ResolveRepositoryName(name)
|
||||
// Resolve the Repository name from fqn to hostname + name
|
||||
hostname, _, err := registry.ResolveRepositoryName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
||||
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||
// If we're not using a custom registry, we know the restrictions
|
||||
// applied to repository names and can warn the user in advance.
|
||||
// Custom repositories can have different rules, and we must also
|
||||
|
@ -1024,7 +1012,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|||
if username == "" {
|
||||
username = "<user>"
|
||||
}
|
||||
return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
|
||||
return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
|
@ -1045,10 +1033,10 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|||
if err := push(authConfig); err != nil {
|
||||
if strings.Contains(err.Error(), "Status 401") {
|
||||
fmt.Fprintln(cli.out, "\nPlease login prior to push:")
|
||||
if err := cli.CmdLogin(endpoint); err != nil {
|
||||
if err := cli.CmdLogin(hostname); err != nil {
|
||||
return err
|
||||
}
|
||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
||||
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||
return push(authConfig)
|
||||
}
|
||||
return err
|
||||
|
@ -1073,8 +1061,8 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
|||
*tag = parsedTag
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to endpoint + name
|
||||
endpoint, _, err := registry.ResolveRepositoryName(remote)
|
||||
// Resolve the Repository name from fqn to hostname + name
|
||||
hostname, _, err := registry.ResolveRepositoryName(remote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1082,7 +1070,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
|||
cli.LoadConfigFile()
|
||||
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
||||
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||
v := url.Values{}
|
||||
v.Set("fromImage", remote)
|
||||
v.Set("tag", *tag)
|
||||
|
@ -1104,10 +1092,10 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
|||
if err := pull(authConfig); err != nil {
|
||||
if strings.Contains(err.Error(), "Status 401") {
|
||||
fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
|
||||
if err := cli.CmdLogin(endpoint); err != nil {
|
||||
if err := cli.CmdLogin(hostname); err != nil {
|
||||
return err
|
||||
}
|
||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
||||
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||
return pull(authConfig)
|
||||
}
|
||||
return err
|
||||
|
@ -1118,11 +1106,11 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
|||
|
||||
func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
cmd := cli.Subcmd("images", "[OPTIONS] [NAME]", "List images")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "only show numeric IDs")
|
||||
all := cmd.Bool([]string{"a", "-all"}, false, "show all images (by default filter out the intermediate images used to build)")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
|
||||
all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate images used to build)")
|
||||
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
|
||||
flViz := cmd.Bool([]string{"v", "#viz", "-viz"}, false, "output graph in graphviz format")
|
||||
flTree := cmd.Bool([]string{"t", "#tree", "-tree"}, false, "output graph in tree format")
|
||||
flViz := cmd.Bool([]string{"v", "#viz", "-viz"}, false, "Output graph in graphviz format")
|
||||
flTree := cmd.Bool([]string{"t", "#tree", "-tree"}, false, "Output graph in tree format")
|
||||
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
|
@ -1309,19 +1297,6 @@ func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix stri
|
|||
}
|
||||
}
|
||||
|
||||
func displayablePorts(ports *engine.Table) string {
|
||||
result := []string{}
|
||||
for _, port := range ports.Data {
|
||||
if port.Get("IP") == "" {
|
||||
result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
|
||||
} else {
|
||||
result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
|
||||
}
|
||||
}
|
||||
sort.Strings(result)
|
||||
return strings.Join(result, ", ")
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdPs(args ...string) error {
|
||||
cmd := cli.Subcmd("ps", "[OPTIONS]", "List containers")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
|
||||
|
@ -1451,11 +1426,11 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
|
|||
v.Set("comment", *flComment)
|
||||
v.Set("author", *flAuthor)
|
||||
var (
|
||||
config *Config
|
||||
config *runconfig.Config
|
||||
env engine.Env
|
||||
)
|
||||
if *flConfig != "" {
|
||||
config = &Config{}
|
||||
config = &runconfig.Config{}
|
||||
if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1616,8 +1591,8 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if !container.State.IsRunning() {
|
||||
return fmt.Errorf("Impossible to attach to a stopped container, start it first")
|
||||
if !container.State.Running {
|
||||
return fmt.Errorf("You cannot attach to a stopped container, start it first")
|
||||
}
|
||||
|
||||
if container.Config.Tty && cli.isTerminal {
|
||||
|
@ -1712,7 +1687,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
|
|||
type ports []int
|
||||
|
||||
func (cli *DockerCli) CmdTag(args ...string) error {
|
||||
cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY[:TAG]", "Tag an image into a repository")
|
||||
cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository")
|
||||
force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil
|
||||
|
@ -1725,7 +1700,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
|
|||
var repository, tag string
|
||||
|
||||
if cmd.NArg() == 3 {
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The format 'IMAGE [REPOSITORY [TAG]]' as been deprecated. Please use IMAGE [REPOSITORY[:TAG]]\n")
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The format 'IMAGE [REPOSITORY [TAG]]' as been deprecated. Please use IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]]\n")
|
||||
repository, tag = cmd.Arg(1), cmd.Arg(2)
|
||||
} else {
|
||||
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
|
||||
|
@ -1745,210 +1720,9 @@ func (cli *DockerCli) CmdTag(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
//FIXME Only used in tests
|
||||
func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
|
||||
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.Usage = nil
|
||||
return parseRun(cmd, args, sysInfo)
|
||||
}
|
||||
|
||||
func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
|
||||
var (
|
||||
// FIXME: use utils.ListOpts for attach and volumes?
|
||||
flAttach = NewListOpts(ValidateAttach)
|
||||
flVolumes = NewListOpts(ValidatePath)
|
||||
flLinks = NewListOpts(ValidateLink)
|
||||
flEnv = NewListOpts(ValidateEnv)
|
||||
|
||||
flPublish ListOpts
|
||||
flExpose ListOpts
|
||||
flDns ListOpts
|
||||
flVolumesFrom ListOpts
|
||||
flLxcOpts ListOpts
|
||||
|
||||
flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
|
||||
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
|
||||
flNetwork = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
|
||||
flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
|
||||
flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
|
||||
flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
|
||||
flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
|
||||
flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
|
||||
flEntrypoint = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
|
||||
flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
|
||||
flMemoryString = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
|
||||
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID")
|
||||
flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
|
||||
flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
||||
|
||||
// For documentation purpose
|
||||
_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
|
||||
_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
|
||||
)
|
||||
|
||||
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
|
||||
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
|
||||
cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
|
||||
cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
|
||||
|
||||
cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", PortSpecTemplateFormat))
|
||||
cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
|
||||
cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
|
||||
cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
|
||||
cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
|
||||
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
|
||||
// Check if the kernel supports memory limit cgroup.
|
||||
if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
|
||||
*flMemoryString = ""
|
||||
}
|
||||
|
||||
// Validate input params
|
||||
if *flDetach && flAttach.Len() > 0 {
|
||||
return nil, nil, cmd, ErrConflictAttachDetach
|
||||
}
|
||||
if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
|
||||
return nil, nil, cmd, ErrInvalidWorikingDirectory
|
||||
}
|
||||
if *flDetach && *flAutoRemove {
|
||||
return nil, nil, cmd, ErrConflictDetachAutoRemove
|
||||
}
|
||||
|
||||
// If neither -d or -a are set, attach to everything by default
|
||||
if flAttach.Len() == 0 && !*flDetach {
|
||||
if !*flDetach {
|
||||
flAttach.Set("stdout")
|
||||
flAttach.Set("stderr")
|
||||
if *flStdin {
|
||||
flAttach.Set("stdin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var flMemory int64
|
||||
if *flMemoryString != "" {
|
||||
parsedMemory, err := utils.RAMInBytes(*flMemoryString)
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
flMemory = parsedMemory
|
||||
}
|
||||
|
||||
var binds []string
|
||||
// add any bind targets to the list of container volumes
|
||||
for bind := range flVolumes.GetMap() {
|
||||
if arr := strings.Split(bind, ":"); len(arr) > 1 {
|
||||
if arr[0] == "/" {
|
||||
return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
|
||||
}
|
||||
dstDir := arr[1]
|
||||
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 '/'")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
parsedArgs = cmd.Args()
|
||||
runCmd []string
|
||||
entrypoint []string
|
||||
image string
|
||||
)
|
||||
if len(parsedArgs) >= 1 {
|
||||
image = cmd.Arg(0)
|
||||
}
|
||||
if len(parsedArgs) > 1 {
|
||||
runCmd = parsedArgs[1:]
|
||||
}
|
||||
if *flEntrypoint != "" {
|
||||
entrypoint = []string{*flEntrypoint}
|
||||
}
|
||||
|
||||
lxcConf, err := parseLxcConfOpts(flLxcOpts)
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
|
||||
var (
|
||||
domainname string
|
||||
hostname = *flHostname
|
||||
parts = strings.SplitN(hostname, ".", 2)
|
||||
)
|
||||
if len(parts) > 1 {
|
||||
hostname = parts[0]
|
||||
domainname = parts[1]
|
||||
}
|
||||
|
||||
ports, portBindings, err := parsePortSpecs(flPublish.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
}
|
||||
|
||||
// Merge in exposed ports to the map of published ports
|
||||
for _, e := range flExpose.GetAll() {
|
||||
if strings.Contains(e, ":") {
|
||||
return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
|
||||
}
|
||||
p := NewPort(splitProtoPort(e))
|
||||
if _, exists := ports[p]; !exists {
|
||||
ports[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
config := &Config{
|
||||
Hostname: hostname,
|
||||
Domainname: domainname,
|
||||
PortSpecs: nil, // Deprecated
|
||||
ExposedPorts: ports,
|
||||
User: *flUser,
|
||||
Tty: *flTty,
|
||||
NetworkDisabled: !*flNetwork,
|
||||
OpenStdin: *flStdin,
|
||||
Memory: flMemory,
|
||||
CpuShares: *flCpuShares,
|
||||
AttachStdin: flAttach.Get("stdin"),
|
||||
AttachStdout: flAttach.Get("stdout"),
|
||||
AttachStderr: flAttach.Get("stderr"),
|
||||
Env: flEnv.GetAll(),
|
||||
Cmd: runCmd,
|
||||
Dns: flDns.GetAll(),
|
||||
Image: image,
|
||||
Volumes: flVolumes.GetMap(),
|
||||
VolumesFrom: strings.Join(flVolumesFrom.GetAll(), ","),
|
||||
Entrypoint: entrypoint,
|
||||
WorkingDir: *flWorkingDir,
|
||||
}
|
||||
|
||||
hostConfig := &HostConfig{
|
||||
Binds: binds,
|
||||
ContainerIDFile: *flContainerIDFile,
|
||||
LxcConf: lxcConf,
|
||||
Privileged: *flPrivileged,
|
||||
PortBindings: portBindings,
|
||||
Links: flLinks.GetAll(),
|
||||
PublishAllPorts: *flPublishAll,
|
||||
}
|
||||
|
||||
if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
|
||||
//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
||||
config.MemorySwap = -1
|
||||
}
|
||||
|
||||
// When allocating stdin in attached mode, close stdin at client disconnect
|
||||
if config.OpenStdin && config.AttachStdin {
|
||||
config.StdinOnce = true
|
||||
}
|
||||
return config, hostConfig, cmd, nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
config, hostConfig, cmd, err := parseRun(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
|
||||
// FIXME: just use runconfig.Parse already
|
||||
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1974,10 +1748,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
var containerIDFile io.WriteCloser
|
||||
if len(hostConfig.ContainerIDFile) > 0 {
|
||||
if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil {
|
||||
return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
|
||||
return fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
|
||||
}
|
||||
if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
|
||||
return fmt.Errorf("failed to create the container ID file: %s", err)
|
||||
return fmt.Errorf("Failed to create the container ID file: %s", err)
|
||||
}
|
||||
defer containerIDFile.Close()
|
||||
}
|
||||
|
@ -1991,20 +1765,15 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false)
|
||||
//if image not found try to pull it
|
||||
if statusCode == 404 {
|
||||
_, tag := utils.ParseRepositoryTag(config.Image)
|
||||
if tag == "" {
|
||||
tag = DEFAULTTAG
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' (tag: %s) locally\n", config.Image, tag)
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
|
||||
|
||||
v := url.Values{}
|
||||
repos, tag := utils.ParseRepositoryTag(config.Image)
|
||||
v.Set("fromImage", repos)
|
||||
v.Set("tag", tag)
|
||||
|
||||
// Resolve the Repository name from fqn to endpoint + name
|
||||
endpoint, _, err := registry.ResolveRepositoryName(repos)
|
||||
// Resolve the Repository name from fqn to hostname + name
|
||||
hostname, _, err := registry.ResolveRepositoryName(repos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2013,7 +1782,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
cli.LoadConfigFile()
|
||||
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := cli.configFile.ResolveAuthConfig(endpoint)
|
||||
authConfig := cli.configFile.ResolveAuthConfig(hostname)
|
||||
buf, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2043,7 +1812,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
|
||||
if len(hostConfig.ContainerIDFile) > 0 {
|
||||
if _, err = containerIDFile.Write([]byte(runResult.Get("Id"))); err != nil {
|
||||
return fmt.Errorf("failed to write the container ID to the file: %s", err)
|
||||
return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2211,6 +1980,9 @@ func (cli *DockerCli) CmdCp(args ...string) error {
|
|||
if stream != nil {
|
||||
defer stream.Close()
|
||||
}
|
||||
if statusCode == 404 {
|
||||
return fmt.Errorf("No such container: %v", info[0])
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2279,7 +2051,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
|||
re := regexp.MustCompile("/+")
|
||||
path = re.ReplaceAllString(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), params)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), params)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
@ -2303,7 +2075,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
|||
}
|
||||
}
|
||||
}
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+VERSION)
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||
req.Host = cli.addr
|
||||
if data != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
@ -2333,7 +2105,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
|||
return nil, -1, err
|
||||
}
|
||||
if len(body) == 0 {
|
||||
return nil, resp.StatusCode, fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
|
||||
return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL)
|
||||
}
|
||||
return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
|
||||
}
|
||||
|
@ -2356,11 +2128,11 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
|||
re := regexp.MustCompile("/+")
|
||||
path = re.ReplaceAllString(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), in)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+VERSION)
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||
req.Host = cli.addr
|
||||
if method == "POST" {
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
|
@ -2375,7 +2147,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
|||
dial, err := net.Dial(cli.proto, cli.addr)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -2384,7 +2156,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
|||
defer clientconn.Close()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -2401,7 +2173,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
|
|||
return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
|
||||
}
|
||||
|
||||
if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
||||
if MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
|
||||
return utils.DisplayJSONMessagesStream(resp.Body, out, cli.terminalFd, cli.isTerminal)
|
||||
}
|
||||
if _, err := io.Copy(out, resp.Body); err != nil {
|
||||
|
@ -2420,18 +2192,18 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
|||
re := regexp.MustCompile("/+")
|
||||
path = re.ReplaceAllString(path, "/")
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", api.APIVERSION, path), nil)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", APIVERSION, path), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+VERSION)
|
||||
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
req.Host = cli.addr
|
||||
|
||||
dial, err := net.Dial(cli.proto, cli.addr)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -2603,7 +2375,7 @@ func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
|
|||
if err := json.Unmarshal(body, c); err != nil {
|
||||
return false, -1, err
|
||||
}
|
||||
return c.State.IsRunning(), c.State.GetExitCode(), nil
|
||||
return c.State.Running, c.State.ExitCode, nil
|
||||
}
|
||||
|
||||
func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) {
|
44
api/common.go
Normal file
44
api/common.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"mime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
APIVERSION = "1.10"
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
)
|
||||
|
||||
func ValidateHost(val string) (string, error) {
|
||||
host, err := utils.ParseHost(DEFAULTHTTPHOST, DEFAULTUNIXSOCKET, val)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
return host, nil
|
||||
}
|
||||
|
||||
//TODO remove, used on < 1.5 in getContainersJSON
|
||||
func displayablePorts(ports *engine.Table) string {
|
||||
result := []string{}
|
||||
for _, port := range ports.Data {
|
||||
if port.Get("IP") == "" {
|
||||
result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
|
||||
} else {
|
||||
result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
|
||||
}
|
||||
}
|
||||
return strings.Join(result, ", ")
|
||||
}
|
||||
|
||||
func MatchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
|
||||
}
|
||||
return err == nil && mimetype == expectedType
|
||||
}
|
18
api/container.go
Normal file
18
api/container.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/nat"
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
Config runconfig.Config
|
||||
HostConfig runconfig.HostConfig
|
||||
State struct {
|
||||
Running bool
|
||||
ExitCode int
|
||||
}
|
||||
NetworkSettings struct {
|
||||
Ports nat.PortMap
|
||||
}
|
||||
}
|
|
@ -10,35 +10,30 @@ import (
|
|||
"fmt"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/pkg/listenbuffer"
|
||||
"github.com/dotcloud/docker/pkg/systemd"
|
||||
"github.com/dotcloud/docker/pkg/user"
|
||||
"github.com/dotcloud/docker/pkg/version"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
APIVERSION = 1.9
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTHTTPPORT = 4243
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
var (
|
||||
activationLock chan struct{}
|
||||
)
|
||||
|
||||
type HttpApiFunc func(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
|
||||
|
||||
func init() {
|
||||
engine.Register("serveapi", ServeApi)
|
||||
}
|
||||
type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error
|
||||
|
||||
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
|
@ -99,6 +94,15 @@ func writeJSON(w http.ResponseWriter, code int, v engine.Env) error {
|
|||
return v.Encode(w)
|
||||
}
|
||||
|
||||
func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if flush {
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
} else {
|
||||
job.Stdout.Add(w)
|
||||
}
|
||||
}
|
||||
|
||||
func getBoolParam(value string) (bool, error) {
|
||||
if value == "" {
|
||||
return false, nil
|
||||
|
@ -110,28 +114,7 @@ func getBoolParam(value string) (bool, error) {
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
//TODO remove, used on < 1.5 in getContainersJSON
|
||||
func displayablePorts(ports *engine.Table) string {
|
||||
result := []string{}
|
||||
for _, port := range ports.Data {
|
||||
if port.Get("IP") == "" {
|
||||
result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
|
||||
} else {
|
||||
result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
|
||||
}
|
||||
}
|
||||
return strings.Join(result, ", ")
|
||||
}
|
||||
|
||||
func MatchesContentType(contentType, expectedType string) bool {
|
||||
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
|
||||
}
|
||||
return err == nil && mimetype == expectedType
|
||||
}
|
||||
|
||||
func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var (
|
||||
authConfig, err = ioutil.ReadAll(r.Body)
|
||||
job = eng.Job("auth")
|
||||
|
@ -154,13 +137,13 @@ func postAuth(eng *engine.Engine, version float64, w http.ResponseWriter, r *htt
|
|||
return nil
|
||||
}
|
||||
|
||||
func getVersion(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
eng.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -178,7 +161,7 @@ func postContainersKill(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -190,7 +173,7 @@ func getContainersExport(eng *engine.Engine, version float64, w http.ResponseWri
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -204,8 +187,8 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
job.Setenv("filter", r.Form.Get("filter"))
|
||||
job.Setenv("all", r.Form.Get("all"))
|
||||
|
||||
if version >= 1.7 {
|
||||
job.Stdout.Add(w)
|
||||
if version.GreaterThanOrEqualTo("1.7") {
|
||||
streamJSON(job, w, false)
|
||||
} else if outs, err = job.Stdout.AddListTable(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -214,7 +197,7 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return err
|
||||
}
|
||||
|
||||
if version < 1.7 && outs != nil { // Convert to legacy format
|
||||
if version.LessThan("1.7") && outs != nil { // Convert to legacy format
|
||||
outsLegacy := engine.NewTable("Created", 0)
|
||||
for _, out := range outs.Data {
|
||||
for _, repoTag := range out.GetList("RepoTags") {
|
||||
|
@ -229,6 +212,7 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
outsLegacy.Add(outLegacy)
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if _, err := outsLegacy.WriteListTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -236,8 +220,8 @@ func getImagesJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version > 1.6 {
|
||||
func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version.GreaterThan("1.6") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return fmt.Errorf("This is now implemented in the client.")
|
||||
}
|
||||
|
@ -245,31 +229,30 @@ func getImagesViz(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func getInfo(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
eng.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEvents(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var job = eng.Job("events", r.RemoteAddr)
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
streamJSON(job, w, true)
|
||||
job.Setenv("since", r.Form.Get("since"))
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
var job = eng.Job("history", vars["name"])
|
||||
job.Stdout.Add(w)
|
||||
streamJSON(job, w, false)
|
||||
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
|
@ -277,18 +260,18 @@ func getImagesHistory(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersChanges(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
var job = eng.Job("changes", vars["name"])
|
||||
job.Stdout.Add(w)
|
||||
streamJSON(job, w, false)
|
||||
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version < 1.4 {
|
||||
func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version.LessThan("1.4") {
|
||||
return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
|
||||
}
|
||||
if vars == nil {
|
||||
|
@ -299,11 +282,11 @@ func getContainersTop(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
}
|
||||
|
||||
job := eng.Job("top", vars["name"], r.Form.Get("ps_args"))
|
||||
job.Stdout.Add(w)
|
||||
streamJSON(job, w, false)
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -319,21 +302,21 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
|
|||
job.Setenv("before", r.Form.Get("before"))
|
||||
job.Setenv("limit", r.Form.Get("limit"))
|
||||
|
||||
if version >= 1.5 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
job.Stdout.Add(w)
|
||||
if version.GreaterThanOrEqualTo("1.5") {
|
||||
streamJSON(job, w, false)
|
||||
} else if outs, err = job.Stdout.AddTable(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if version < 1.5 { // Convert to legacy format
|
||||
if version.LessThan("1.5") { // Convert to legacy format
|
||||
for _, out := range outs.Data {
|
||||
ports := engine.NewTable("", 0)
|
||||
ports.ReadListFrom([]byte(out.Get("Ports")))
|
||||
out.Set("Ports", displayablePorts(ports))
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if _, err = outs.WriteListTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -341,7 +324,7 @@ func getContainersJSON(eng *engine.Engine, version float64, w http.ResponseWrite
|
|||
return nil
|
||||
}
|
||||
|
||||
func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -358,7 +341,7 @@ func postImagesTag(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -367,7 +350,7 @@ func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *h
|
|||
env engine.Env
|
||||
job = eng.Job("commit", r.Form.Get("container"))
|
||||
)
|
||||
if err := config.Import(r.Body); err != nil {
|
||||
if err := config.Decode(r.Body); err != nil {
|
||||
utils.Errorf("%s", err)
|
||||
}
|
||||
|
||||
|
@ -387,7 +370,7 @@ func postCommit(eng *engine.Engine, version float64, w http.ResponseWriter, r *h
|
|||
}
|
||||
|
||||
// Creates an image from Pull or from Import
|
||||
func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -407,9 +390,6 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
authConfig = &auth.AuthConfig{}
|
||||
}
|
||||
}
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
if image != "" { //pull
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
|
@ -418,7 +398,7 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
}
|
||||
}
|
||||
job = eng.Job("pull", r.Form.Get("fromImage"), tag)
|
||||
job.SetenvBool("parallel", version > 1.3)
|
||||
job.SetenvBool("parallel", version.GreaterThan("1.3"))
|
||||
job.SetenvJson("metaHeaders", metaHeaders)
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
} else { //import
|
||||
|
@ -426,20 +406,24 @@ func postImagesCreate(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
job.Stdin.Add(r.Body)
|
||||
}
|
||||
|
||||
job.SetenvBool("json", version > 1.0)
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
if version.GreaterThan("1.0") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, true)
|
||||
} else {
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -466,37 +450,37 @@ func getImagesSearch(eng *engine.Engine, version float64, w http.ResponseWriter,
|
|||
var job = eng.Job("search", r.Form.Get("term"))
|
||||
job.SetenvJson("metaHeaders", metaHeaders)
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
job.Stdout.Add(w)
|
||||
streamJSON(job, w, false)
|
||||
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func postImagesInsert(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesInsert(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
job := eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path"))
|
||||
job.SetenvBool("json", version > 1.0)
|
||||
job.Stdout.Add(w)
|
||||
if version.GreaterThan("1.0") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, false)
|
||||
} else {
|
||||
job.Stdout.Add(w)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -527,30 +511,31 @@ func postImagesPush(eng *engine.Engine, version float64, w http.ResponseWriter,
|
|||
}
|
||||
}
|
||||
|
||||
if version > 1.0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
job := eng.Job("push", vars["name"])
|
||||
job.SetenvJson("metaHeaders", metaHeaders)
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
job.SetenvBool("json", version > 1.0)
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
if version.GreaterThan("1.0") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, true)
|
||||
} else {
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
}
|
||||
|
||||
if err := job.Run(); err != nil {
|
||||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
if version > 1.0 {
|
||||
if version.GreaterThan("1.0") {
|
||||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
}
|
||||
job := eng.Job("image_export", vars["name"])
|
||||
|
@ -558,13 +543,13 @@ func getImagesGet(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return job.Run()
|
||||
}
|
||||
|
||||
func postImagesLoad(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
job := eng.Job("load")
|
||||
job.Stdin.Add(r.Body)
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -595,7 +580,7 @@ func postContainersCreate(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
return writeJSON(w, http.StatusCreated, out)
|
||||
}
|
||||
|
||||
func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -611,7 +596,7 @@ func postContainersRestart(eng *engine.Engine, version float64, w http.ResponseW
|
|||
return nil
|
||||
}
|
||||
|
||||
func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -621,6 +606,7 @@ func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
job := eng.Job("container_delete", vars["name"])
|
||||
job.Setenv("removeVolume", r.Form.Get("v"))
|
||||
job.Setenv("removeLink", r.Form.Get("link"))
|
||||
job.Setenv("forceRemove", r.Form.Get("force"))
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -628,7 +614,7 @@ func deleteContainers(eng *engine.Engine, version float64, w http.ResponseWriter
|
|||
return nil
|
||||
}
|
||||
|
||||
func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -636,13 +622,13 @@ func deleteImages(eng *engine.Engine, version float64, w http.ResponseWriter, r
|
|||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
var job = eng.Job("image_delete", vars["name"])
|
||||
job.Stdout.Add(w)
|
||||
job.SetenvBool("autoPrune", version > 1.1)
|
||||
streamJSON(job, w, false)
|
||||
job.Setenv("force", r.Form.Get("force"))
|
||||
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -663,7 +649,7 @@ func postContainersStart(eng *engine.Engine, version float64, w http.ResponseWri
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -679,7 +665,7 @@ func postContainersStop(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -701,7 +687,7 @@ func postContainersWait(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return writeJSON(w, http.StatusOK, env)
|
||||
}
|
||||
|
||||
func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -714,7 +700,7 @@ func postContainersResize(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
return nil
|
||||
}
|
||||
|
||||
func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -756,7 +742,7 @@ func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
|
||||
fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
|
||||
if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version >= 1.6 {
|
||||
if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
|
||||
errStream = utils.NewStdWriter(outStream, utils.Stderr)
|
||||
outStream = utils.NewStdWriter(outStream, utils.Stdout)
|
||||
} else {
|
||||
|
@ -779,7 +765,7 @@ func postContainersAttach(eng *engine.Engine, version float64, w http.ResponseWr
|
|||
return nil
|
||||
}
|
||||
|
||||
func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -811,28 +797,28 @@ func wsContainersAttach(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
return nil
|
||||
}
|
||||
|
||||
func getContainersByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
var job = eng.Job("inspect", vars["name"], "container")
|
||||
job.Stdout.Add(w)
|
||||
streamJSON(job, w, false)
|
||||
job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func getImagesByName(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
var job = eng.Job("inspect", vars["name"], "image")
|
||||
job.Stdout.Add(w)
|
||||
streamJSON(job, w, false)
|
||||
job.SetenvBool("conflict", true) //conflict=true to detect conflict between containers and images in the job
|
||||
return job.Run()
|
||||
}
|
||||
|
||||
func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version < 1.3 {
|
||||
func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if version.LessThan("1.3") {
|
||||
return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
|
||||
}
|
||||
var (
|
||||
|
@ -847,7 +833,7 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
|
|||
// Both headers will be parsed and sent along to the daemon, but if a non-empty
|
||||
// ConfigFile is present, any value provided as an AuthConfig directly will
|
||||
// be overridden. See BuildFile::CmdFrom for details.
|
||||
if version < 1.9 && authEncoded != "" {
|
||||
if version.LessThan("1.9") && authEncoded != "" {
|
||||
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
|
@ -865,30 +851,32 @@ func postBuild(eng *engine.Engine, version float64, w http.ResponseWriter, r *ht
|
|||
}
|
||||
}
|
||||
|
||||
if version >= 1.8 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if version.GreaterThanOrEqualTo("1.8") {
|
||||
job.SetenvBool("json", true)
|
||||
streamJSON(job, w, true)
|
||||
} else {
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
}
|
||||
|
||||
job.Stdout.Add(utils.NewWriteFlusher(w))
|
||||
job.Stdin.Add(r.Body)
|
||||
job.Setenv("remote", r.FormValue("remote"))
|
||||
job.Setenv("t", r.FormValue("t"))
|
||||
job.Setenv("q", r.FormValue("q"))
|
||||
job.Setenv("nocache", r.FormValue("nocache"))
|
||||
job.Setenv("rm", r.FormValue("rm"))
|
||||
job.SetenvJson("authConfig", authConfig)
|
||||
job.SetenvJson("configFile", configFile)
|
||||
|
||||
if err := job.Run(); err != nil {
|
||||
if !job.Stdout.Used() {
|
||||
return err
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version >= 1.8)
|
||||
sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
|
||||
w.Write(sf.FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if vars == nil {
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
@ -914,11 +902,14 @@ func postContainersCopy(eng *engine.Engine, version float64, w http.ResponseWrit
|
|||
job.Stdout.Add(w)
|
||||
if err := job.Run(); err != nil {
|
||||
utils.Errorf("%s", err.Error())
|
||||
if strings.Contains(err.Error(), "No such container") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func optionsHandler(eng *engine.Engine, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
}
|
||||
|
@ -928,7 +919,7 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
|
|||
w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
|
||||
}
|
||||
|
||||
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion string) http.HandlerFunc {
|
||||
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// log the request
|
||||
utils.Debugf("Calling %s %s", localMethod, localRoute)
|
||||
|
@ -939,20 +930,20 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local
|
|||
|
||||
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
||||
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
||||
if len(userAgent) == 2 && userAgent[1] != dockerVersion {
|
||||
if len(userAgent) == 2 && !dockerVersion.Equal(userAgent[1]) {
|
||||
utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
|
||||
}
|
||||
}
|
||||
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
|
||||
if err != nil {
|
||||
version := version.Version(mux.Vars(r)["version"])
|
||||
if version == "" {
|
||||
version = APIVERSION
|
||||
}
|
||||
if enableCors {
|
||||
writeCorsHeaders(w, r)
|
||||
}
|
||||
|
||||
if version == 0 || version > APIVERSION {
|
||||
http.Error(w, fmt.Errorf("client and server don't have same version (client : %g, server: %g)", version, APIVERSION).Error(), http.StatusNotFound)
|
||||
if version.GreaterThan(APIVERSION) {
|
||||
http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, APIVERSION).Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1050,7 +1041,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
|||
localMethod := method
|
||||
|
||||
// build the handler function
|
||||
f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, dockerVersion)
|
||||
f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, enableCors, version.Version(dockerVersion))
|
||||
|
||||
// add the new route
|
||||
if localRoute == "" {
|
||||
|
@ -1068,13 +1059,13 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
|||
// ServeRequest processes a single http request to the docker remote api.
|
||||
// FIXME: refactor this to be part of Server and not require re-creating a new
|
||||
// router each time. This requires first moving ListenAndServe into Server.
|
||||
func ServeRequest(eng *engine.Engine, apiversion float64, w http.ResponseWriter, req *http.Request) error {
|
||||
func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) error {
|
||||
router, err := createRouter(eng, false, true, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert APIVERSION into the request as a convenience
|
||||
req.URL.Path = fmt.Sprintf("/v%g%s", apiversion, req.URL.Path)
|
||||
req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
|
||||
router.ServeHTTP(w, req)
|
||||
return nil
|
||||
}
|
||||
|
@ -1089,6 +1080,11 @@ func ServeFd(addr string, handle http.Handler) error {
|
|||
|
||||
chErrors := make(chan error, len(ls))
|
||||
|
||||
// We don't want to start serving on these sockets until the
|
||||
// "initserver" job has completed. Otherwise required handlers
|
||||
// won't be ready.
|
||||
<-activationLock
|
||||
|
||||
// Since ListenFD will return one or more sockets we have
|
||||
// to create a go func to spawn off multiple serves
|
||||
for i := range ls {
|
||||
|
@ -1109,10 +1105,34 @@ func ServeFd(addr string, handle http.Handler) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func lookupGidByName(nameOrGid string) (int, error) {
|
||||
groups, err := user.ParseGroupFilter(func(g *user.Group) bool {
|
||||
return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
|
||||
})
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if groups != nil && len(groups) > 0 {
|
||||
return groups[0].Gid, nil
|
||||
}
|
||||
return -1, fmt.Errorf("Group %s not found", nameOrGid)
|
||||
}
|
||||
|
||||
func changeGroup(addr string, nameOrGid string) error {
|
||||
gid, err := lookupGidByName(nameOrGid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utils.Debugf("%s group found. gid: %d", nameOrGid, gid)
|
||||
return os.Chown(addr, 0, gid)
|
||||
}
|
||||
|
||||
// ListenAndServe sets up the required http.Server and gets it listening for
|
||||
// each addr passed in and does protocol specific checking.
|
||||
func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string) error {
|
||||
func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string, socketGroup string) error {
|
||||
r, err := createRouter(eng, logging, enableCors, dockerVersion)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1127,7 +1147,7 @@ func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors
|
|||
}
|
||||
}
|
||||
|
||||
l, err := net.Listen(proto, addr)
|
||||
l, err := listenbuffer.NewListenBuffer(proto, addr, activationLock, 15*time.Minute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1143,19 +1163,14 @@ func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors
|
|||
return err
|
||||
}
|
||||
|
||||
groups, err := ioutil.ReadFile("/etc/group")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
|
||||
if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
|
||||
gid, err := strconv.Atoi(gidMatch[2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utils.Debugf("docker group found. gid: %d", gid)
|
||||
if err := os.Chown(addr, 0, gid); err != nil {
|
||||
return err
|
||||
if socketGroup != "" {
|
||||
if err := changeGroup(addr, socketGroup); err != nil {
|
||||
if socketGroup == "docker" {
|
||||
// if the user hasn't explicitly specified the group ownership, don't fail on errors.
|
||||
utils.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error())
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
@ -1169,14 +1184,21 @@ func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors
|
|||
// ServeApi loops through all of the protocols sent in to docker and spawns
|
||||
// off a go routine to setup a serving http.Server for each.
|
||||
func ServeApi(job *engine.Job) engine.Status {
|
||||
protoAddrs := job.Args
|
||||
chErrors := make(chan error, len(protoAddrs))
|
||||
var (
|
||||
protoAddrs = job.Args
|
||||
chErrors = make(chan error, len(protoAddrs))
|
||||
)
|
||||
activationLock = make(chan struct{})
|
||||
|
||||
if err := job.Eng.Register("acceptconnections", AcceptConnections); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
for _, protoAddr := range protoAddrs {
|
||||
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
||||
go func() {
|
||||
log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
|
||||
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
|
||||
chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"), job.Getenv("SocketGroup"))
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -1187,8 +1209,15 @@ func ServeApi(job *engine.Job) engine.Status {
|
|||
}
|
||||
}
|
||||
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
func AcceptConnections(job *engine.Job) engine.Status {
|
||||
// Tell the init daemon we are accepting requests
|
||||
go systemd.SdNotify("READY=1")
|
||||
|
||||
// close the lock so the listeners start accepting connections
|
||||
close(activationLock)
|
||||
|
||||
return engine.StatusOK
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/pkg/system"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -19,9 +20,10 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
Archive io.Reader
|
||||
Compression int
|
||||
TarOptions struct {
|
||||
Archive io.ReadCloser
|
||||
ArchiveReader io.Reader
|
||||
Compression int
|
||||
TarOptions struct {
|
||||
Includes []string
|
||||
Compression Compression
|
||||
}
|
||||
|
@ -65,13 +67,13 @@ func DetectCompression(source []byte) Compression {
|
|||
return Uncompressed
|
||||
}
|
||||
|
||||
func xzDecompress(archive io.Reader) (io.Reader, error) {
|
||||
func xzDecompress(archive io.Reader) (io.ReadCloser, error) {
|
||||
args := []string{"xz", "-d", "-c", "-q"}
|
||||
|
||||
return CmdStream(exec.Command(args[0], args[1:]...), archive)
|
||||
}
|
||||
|
||||
func DecompressStream(archive io.Reader) (io.Reader, error) {
|
||||
func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
|
||||
buf := make([]byte, 10)
|
||||
totalN := 0
|
||||
for totalN < 10 {
|
||||
|
@ -90,11 +92,11 @@ func DecompressStream(archive io.Reader) (io.Reader, error) {
|
|||
|
||||
switch compression {
|
||||
case Uncompressed:
|
||||
return wrap, nil
|
||||
return ioutil.NopCloser(wrap), nil
|
||||
case Gzip:
|
||||
return gzip.NewReader(wrap)
|
||||
case Bzip2:
|
||||
return bzip2.NewReader(wrap), nil
|
||||
return ioutil.NopCloser(bzip2.NewReader(wrap)), nil
|
||||
case Xz:
|
||||
return xzDecompress(wrap)
|
||||
default:
|
||||
|
@ -106,7 +108,7 @@ func CompressStream(dest io.WriteCloser, compression Compression) (io.WriteClose
|
|||
|
||||
switch compression {
|
||||
case Uncompressed:
|
||||
return dest, nil
|
||||
return utils.NopWriteCloser(dest), nil
|
||||
case Gzip:
|
||||
return gzip.NewWriter(dest), nil
|
||||
case Bzip2, Xz:
|
||||
|
@ -164,6 +166,13 @@ func addTarFile(path, name string, tw *tar.Writer) error {
|
|||
hdr.Devmajor = int64(major(uint64(stat.Rdev)))
|
||||
hdr.Devminor = int64(minor(uint64(stat.Rdev)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
capability, _ := system.Lgetxattr(path, "security.capability")
|
||||
if capability != nil {
|
||||
hdr.Xattrs = make(map[string]string)
|
||||
hdr.Xattrs["security.capability"] = string(capability)
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
|
@ -185,20 +194,25 @@ func addTarFile(path, name string, tw *tar.Writer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) error {
|
||||
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader) error {
|
||||
// hdr.Mode is in linux format, which we can use for sycalls,
|
||||
// but for os.Foo() calls we need the mode converted to os.FileMode,
|
||||
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
|
||||
hdrInfo := hdr.FileInfo()
|
||||
|
||||
switch hdr.Typeflag {
|
||||
case tar.TypeDir:
|
||||
// Create directory unless it exists as a directory already.
|
||||
// In that case we just want to merge the two
|
||||
if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
|
||||
if err := os.Mkdir(path, os.FileMode(hdr.Mode)); err != nil {
|
||||
if err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case tar.TypeReg, tar.TypeRegA:
|
||||
// Source is regular file
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode))
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -245,10 +259,16 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader)
|
|||
return err
|
||||
}
|
||||
|
||||
for key, value := range hdr.Xattrs {
|
||||
if err := system.Lsetxattr(path, key, []byte(value), 0); 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 {
|
||||
if err := os.Chmod(path, os.FileMode(hdr.Mode&07777)); err != nil {
|
||||
if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -256,11 +276,11 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader)
|
|||
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 := UtimesNano(path, ts); err != nil {
|
||||
if err := system.UtimesNano(path, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := LUtimesNano(path, ts); err != nil {
|
||||
if err := system.LUtimesNano(path, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -269,7 +289,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader)
|
|||
|
||||
// Tar creates an archive from the directory at `path`, and returns it as a
|
||||
// stream of bytes.
|
||||
func Tar(path string, compression Compression) (io.Reader, error) {
|
||||
func Tar(path string, compression Compression) (io.ReadCloser, error) {
|
||||
return TarFilter(path, &TarOptions{Compression: compression})
|
||||
}
|
||||
|
||||
|
@ -291,7 +311,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(srcPath string, options *TarOptions) (io.Reader, error) {
|
||||
func TarFilter(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
|
||||
compressWriter, err := CompressStream(pipeWriter, options.Compression)
|
||||
|
@ -337,6 +357,9 @@ func TarFilter(srcPath string, options *TarOptions) (io.Reader, error) {
|
|||
if err := compressWriter.Close(); err != nil {
|
||||
utils.Debugf("Can't close compress writer: %s\n", err)
|
||||
}
|
||||
if err := pipeWriter.Close(); err != nil {
|
||||
utils.Debugf("Can't close pipe writer: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return pipeReader, nil
|
||||
|
@ -352,12 +375,13 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
|
|||
return fmt.Errorf("Empty archive")
|
||||
}
|
||||
|
||||
archive, err := DecompressStream(archive)
|
||||
decompressedArchive, err := DecompressStream(archive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer decompressedArchive.Close()
|
||||
|
||||
tr := tar.NewReader(archive)
|
||||
tr := tar.NewReader(decompressedArchive)
|
||||
|
||||
var dirs []*tar.Header
|
||||
|
||||
|
@ -432,15 +456,19 @@ func TarUntar(src string, dst string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
return Untar(archive, dst, nil)
|
||||
}
|
||||
|
||||
// UntarPath is a convenience function which looks for an archive
|
||||
// at filesystem path `src`, and unpacks it at `dst`.
|
||||
func UntarPath(src, dst string) error {
|
||||
if archive, err := os.Open(src); err != nil {
|
||||
archive, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if err := Untar(archive, dst, nil); err != nil {
|
||||
}
|
||||
defer archive.Close()
|
||||
if err := Untar(archive, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -528,7 +556,7 @@ func CopyFileWithTar(src, dst string) (err 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 io.Reader) (io.Reader, error) {
|
||||
func CmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) {
|
||||
if input != nil {
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -67,12 +67,13 @@ func tarUntar(t *testing.T, origin string, compression Compression) error {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
buf := make([]byte, 10)
|
||||
if _, err := archive.Read(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
archive = io.MultiReader(bytes.NewReader(buf), archive)
|
||||
wrap := io.MultiReader(bytes.NewReader(buf), archive)
|
||||
|
||||
detectedCompression := DetectCompression(buf)
|
||||
if detectedCompression.Extension() != compression.Extension() {
|
||||
|
@ -84,7 +85,7 @@ func tarUntar(t *testing.T, origin string, compression Compression) error {
|
|||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
if err := Untar(archive, tmp, nil); err != nil {
|
||||
if err := Untar(wrap, tmp, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(tmp); err != nil {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/pkg/system"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -126,10 +128,11 @@ func Changes(layers []string, rw string) ([]Change, error) {
|
|||
}
|
||||
|
||||
type FileInfo struct {
|
||||
parent *FileInfo
|
||||
name string
|
||||
stat syscall.Stat_t
|
||||
children map[string]*FileInfo
|
||||
parent *FileInfo
|
||||
name string
|
||||
stat syscall.Stat_t
|
||||
children map[string]*FileInfo
|
||||
capability []byte
|
||||
}
|
||||
|
||||
func (root *FileInfo) LookUp(path string) *FileInfo {
|
||||
|
@ -200,7 +203,8 @@ 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) ||
|
||||
!sameFsTimeSpec(getLastModification(oldStat), getLastModification(newStat)) {
|
||||
!sameFsTimeSpec(system.GetLastModification(oldStat), system.GetLastModification(newStat)) ||
|
||||
bytes.Compare(oldChild.capability, newChild.capability) != 0 {
|
||||
change := Change{
|
||||
Path: newChild.path(),
|
||||
Kind: ChangeModify,
|
||||
|
@ -275,6 +279,8 @@ func collectFileInfo(sourceDir string) (*FileInfo, error) {
|
|||
return err
|
||||
}
|
||||
|
||||
info.capability, _ = system.Lgetxattr(path, "security.capability")
|
||||
|
||||
parent.children[info.name] = info
|
||||
|
||||
return nil
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package archive
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -28,7 +30,7 @@ func timeToTimespec(time time.Time) (ts syscall.Timespec) {
|
|||
|
||||
// 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 {
|
||||
func ApplyLayer(dest string, layer ArchiveReader) error {
|
||||
// We need to be able to set any perms
|
||||
oldmask := syscall.Umask(0)
|
||||
defer syscall.Umask(oldmask)
|
||||
|
@ -42,6 +44,9 @@ func ApplyLayer(dest string, layer Archive) error {
|
|||
|
||||
var dirs []*tar.Header
|
||||
|
||||
aufsTempdir := ""
|
||||
aufsHardlinks := make(map[string]*tar.Header)
|
||||
|
||||
// Iterate through the files in the archive.
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
|
@ -72,6 +77,22 @@ func ApplyLayer(dest string, layer Archive) error {
|
|||
|
||||
// Skip AUFS metadata dirs
|
||||
if strings.HasPrefix(hdr.Name, ".wh..wh.") {
|
||||
// Regular files inside /.wh..wh.plnk can be used as hardlink targets
|
||||
// We don't want this directory, but we need the files in them so that
|
||||
// such hardlinks can be resolved.
|
||||
if strings.HasPrefix(hdr.Name, ".wh..wh.plnk") && hdr.Typeflag == tar.TypeReg {
|
||||
basename := filepath.Base(hdr.Name)
|
||||
aufsHardlinks[basename] = hdr
|
||||
if aufsTempdir == "" {
|
||||
if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(aufsTempdir)
|
||||
}
|
||||
if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -96,7 +117,26 @@ func ApplyLayer(dest string, layer Archive) error {
|
|||
}
|
||||
}
|
||||
|
||||
if err := createTarFile(path, dest, hdr, tr); err != nil {
|
||||
srcData := io.Reader(tr)
|
||||
srcHdr := hdr
|
||||
|
||||
// Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so
|
||||
// we manually retarget these into the temporary files we extracted them into
|
||||
if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") {
|
||||
linkBasename := filepath.Base(hdr.Linkname)
|
||||
srcHdr = aufsHardlinks[linkBasename]
|
||||
if srcHdr == nil {
|
||||
return fmt.Errorf("Invalid aufs hardlink")
|
||||
}
|
||||
tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
srcData = tmpFile
|
||||
}
|
||||
|
||||
if err := createTarFile(path, dest, srcHdr, srcData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
59
archive/wrap.go
Normal file
59
archive/wrap.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package archive
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Generate generates a new archive from the content provided
|
||||
// as input.
|
||||
//
|
||||
// `files` is a sequence of path/content pairs. A new file is
|
||||
// added to the archive for each pair.
|
||||
// If the last pair is incomplete, the file is created with an
|
||||
// empty content. For example:
|
||||
//
|
||||
// Generate("foo.txt", "hello world", "emptyfile")
|
||||
//
|
||||
// The above call will return an archive with 2 files:
|
||||
// * ./foo.txt with content "hello world"
|
||||
// * ./empty with empty content
|
||||
//
|
||||
// FIXME: stream content instead of buffering
|
||||
// FIXME: specify permissions and other archive metadata
|
||||
func Generate(input ...string) (Archive, error) {
|
||||
files := parseStringPairs(input...)
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
for _, file := range files {
|
||||
name, content := file[0], file[1]
|
||||
hdr := &tar.Header{
|
||||
Name: name,
|
||||
Size: int64(len(content)),
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := tw.Write([]byte(content)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := tw.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.NopCloser(buf), nil
|
||||
}
|
||||
|
||||
func parseStringPairs(input ...string) (output [][2]string) {
|
||||
output = make([][2]string, 0, len(input)/2+1)
|
||||
for i := 0; i < len(input); i += 2 {
|
||||
var pair [2]string
|
||||
pair[0] = input[i]
|
||||
if i+1 < len(input) {
|
||||
pair[1] = input[i+1]
|
||||
}
|
||||
output = append(output, pair)
|
||||
}
|
||||
return
|
||||
}
|
59
auth/auth.go
59
auth/auth.go
|
@ -252,50 +252,39 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
|
|||
}
|
||||
|
||||
// this method matches a auth configuration to a server address or a url
|
||||
func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig {
|
||||
if registry == IndexServerAddress() || len(registry) == 0 {
|
||||
func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
|
||||
if hostname == IndexServerAddress() || len(hostname) == 0 {
|
||||
// default to the index server
|
||||
return config.Configs[IndexServerAddress()]
|
||||
}
|
||||
// if it's not the index server there are three cases:
|
||||
//
|
||||
// 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
|
||||
|
||||
swapProtocol := func(url string) string {
|
||||
if strings.HasPrefix(url, "http:") {
|
||||
return strings.Replace(url, "http:", "https:", 1)
|
||||
}
|
||||
if strings.HasPrefix(url, "https:") {
|
||||
return strings.Replace(url, "https:", "http:", 1)
|
||||
}
|
||||
return url
|
||||
// First try the happy case
|
||||
if c, found := config.Configs[hostname]; found {
|
||||
return c
|
||||
}
|
||||
|
||||
resolveIgnoringProtocol := func(url string) AuthConfig {
|
||||
if c, found := config.Configs[url]; found {
|
||||
return c
|
||||
convertToHostname := func(url string) string {
|
||||
stripped := url
|
||||
if strings.HasPrefix(url, "http://") {
|
||||
stripped = strings.Replace(url, "http://", "", 1)
|
||||
} else if strings.HasPrefix(url, "https://") {
|
||||
stripped = strings.Replace(url, "https://", "", 1)
|
||||
}
|
||||
registrySwappedProtocol := swapProtocol(url)
|
||||
// now try to match with the different protocol
|
||||
if c, found := config.Configs[registrySwappedProtocol]; found {
|
||||
return c
|
||||
}
|
||||
return AuthConfig{}
|
||||
|
||||
nameParts := strings.SplitN(stripped, "/", 2)
|
||||
|
||||
return nameParts[0]
|
||||
}
|
||||
|
||||
// match both protocols as it could also be a server name like httpfoo
|
||||
if strings.HasPrefix(registry, "http:") || strings.HasPrefix(registry, "https:") {
|
||||
return resolveIgnoringProtocol(registry)
|
||||
// Maybe they have a legacy config file, we will iterate the keys converting
|
||||
// them to the new format and testing
|
||||
normalizedHostename := convertToHostname(hostname)
|
||||
for registry, config := range config.Configs {
|
||||
if registryHostname := convertToHostname(registry); registryHostname == normalizedHostename {
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
url := "https://" + registry
|
||||
if !strings.Contains(registry, "/") {
|
||||
url = url + "/v1/"
|
||||
}
|
||||
return resolveIgnoringProtocol(url)
|
||||
// When all else fails, return an empty auth config
|
||||
return AuthConfig{}
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
|
|||
}
|
||||
configFile.Configs["https://registry.example.com/v1/"] = registryAuth
|
||||
configFile.Configs["http://localhost:8000/v1/"] = localAuth
|
||||
configFile.Configs["registry.com"] = registryAuth
|
||||
|
||||
validRegistries := map[string][]string{
|
||||
"https://registry.example.com/v1/": {
|
||||
|
@ -122,6 +123,12 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
|
|||
"localhost:8000",
|
||||
"localhost:8000/v1/",
|
||||
},
|
||||
"registry.com": {
|
||||
"https://registry.com/v1/",
|
||||
"http://registry.com/v1/",
|
||||
"registry.com",
|
||||
"registry.com/v1/",
|
||||
},
|
||||
}
|
||||
|
||||
for configKey, registries := range validRegistries {
|
||||
|
|
112
buildfile.go
112
buildfile.go
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -38,7 +39,7 @@ type buildFile struct {
|
|||
|
||||
image string
|
||||
maintainer string
|
||||
config *Config
|
||||
config *runconfig.Config
|
||||
|
||||
contextPath string
|
||||
context *utils.TarSum
|
||||
|
@ -64,8 +65,11 @@ type buildFile struct {
|
|||
func (b *buildFile) clearTmp(containers map[string]struct{}) {
|
||||
for c := range containers {
|
||||
tmp := b.runtime.Get(c)
|
||||
b.runtime.Destroy(tmp)
|
||||
fmt.Fprintf(b.outStream, "Removing intermediate container %s\n", utils.TruncateID(c))
|
||||
if err := b.runtime.Destroy(tmp); err != nil {
|
||||
fmt.Fprintf(b.outStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(b.outStream, "Removing intermediate container %s\n", utils.TruncateID(c))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,18 +105,26 @@ func (b *buildFile) CmdFrom(name string) error {
|
|||
}
|
||||
}
|
||||
b.image = image.ID
|
||||
b.config = &Config{}
|
||||
b.config = &runconfig.Config{}
|
||||
if image.Config != nil {
|
||||
b.config = image.Config
|
||||
}
|
||||
if b.config.Env == nil || len(b.config.Env) == 0 {
|
||||
b.config.Env = append(b.config.Env, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
|
||||
b.config.Env = append(b.config.Env, "HOME=/", "PATH="+defaultPathEnv)
|
||||
}
|
||||
// Process ONBUILD triggers if they exist
|
||||
if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
|
||||
fmt.Fprintf(b.errStream, "# Executing %d build triggers\n", nTriggers)
|
||||
}
|
||||
for n, step := range b.config.OnBuild {
|
||||
splitStep := strings.Split(step, " ")
|
||||
stepInstruction := strings.ToUpper(strings.Trim(splitStep[0], " "))
|
||||
switch stepInstruction {
|
||||
case "ONBUILD":
|
||||
return fmt.Errorf("Source image contains forbidden chained `ONBUILD ONBUILD` trigger: %s", step)
|
||||
case "MAINTAINER", "FROM":
|
||||
return fmt.Errorf("Source image contains forbidden %s trigger: %s", stepInstruction, step)
|
||||
}
|
||||
if err := b.BuildStep(fmt.Sprintf("onbuild-%d", n), step); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -124,6 +136,14 @@ func (b *buildFile) CmdFrom(name string) error {
|
|||
// The ONBUILD command declares a build instruction to be executed in any future build
|
||||
// using the current image as a base.
|
||||
func (b *buildFile) CmdOnbuild(trigger string) error {
|
||||
splitTrigger := strings.Split(trigger, " ")
|
||||
triggerInstruction := strings.ToUpper(strings.Trim(splitTrigger[0], " "))
|
||||
switch triggerInstruction {
|
||||
case "ONBUILD":
|
||||
return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
||||
case "MAINTAINER", "FROM":
|
||||
return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
|
||||
}
|
||||
b.config.OnBuild = append(b.config.OnBuild, trigger)
|
||||
return b.commit("", b.config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
|
||||
}
|
||||
|
@ -158,14 +178,14 @@ func (b *buildFile) CmdRun(args string) error {
|
|||
if b.image == "" {
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
config, _, _, err := ParseRun(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
|
||||
config, _, _, err := runconfig.Parse(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := b.config.Cmd
|
||||
b.config.Cmd = nil
|
||||
MergeConfig(b.config, config)
|
||||
runconfig.Merge(b.config, config)
|
||||
|
||||
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
|
||||
|
||||
|
@ -179,11 +199,20 @@ func (b *buildFile) CmdRun(args string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
cid, err := b.run()
|
||||
c, err := b.create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.commit(cid, cmd, "run"); err != nil {
|
||||
// Ensure that we keep the container mounted until the commit
|
||||
// to avoid unmounting and then mounting directly again
|
||||
c.Mount()
|
||||
defer c.Unmount()
|
||||
|
||||
err = b.run(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.commit(c.ID, cmd, "run"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -342,7 +371,7 @@ func (b *buildFile) checkPathForAddition(orig string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
||||
func (b *buildFile) addContext(container *Container, orig, dest string, remote bool) error {
|
||||
var (
|
||||
origPath = path.Join(b.contextPath, orig)
|
||||
destPath = path.Join(container.BasefsPath(), dest)
|
||||
|
@ -358,20 +387,39 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
if err := archive.CopyWithTar(origPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
// First try to unpack the source as an archive
|
||||
} else if err := archive.UntarPath(origPath, destPath); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// First try to unpack the source as an archive
|
||||
// to support the untar feature we need to clean up the path a little bit
|
||||
// because tar is very forgiving. First we need to strip off the archive's
|
||||
// filename from the path but this is only added if it does not end in / .
|
||||
tarDest := destPath
|
||||
if strings.HasSuffix(tarDest, "/") {
|
||||
tarDest = filepath.Dir(destPath)
|
||||
}
|
||||
|
||||
// If we are adding a remote file, do not try to untar it
|
||||
if !remote {
|
||||
// try to successfully untar the orig
|
||||
if err := archive.UntarPath(origPath, tarDest); err == nil {
|
||||
return nil
|
||||
}
|
||||
utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
|
||||
// If that fails, just copy it as a regular file
|
||||
if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := archive.CopyWithTar(origPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If that fails, just copy it as a regular file
|
||||
// but do not use all the magic path handling for the tar path
|
||||
if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := archive.CopyWithTar(origPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -399,14 +447,15 @@ func (b *buildFile) CmdAdd(args string) error {
|
|||
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
|
||||
remoteHash string
|
||||
isRemote bool
|
||||
)
|
||||
|
||||
if utils.IsURL(orig) {
|
||||
isRemote = true
|
||||
resp, err := utils.Download(orig)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -435,6 +484,7 @@ func (b *buildFile) CmdAdd(args string) error {
|
|||
}
|
||||
tarSum := utils.TarSum{Reader: r, DisableCompression: true}
|
||||
remoteHash = tarSum.Sum(nil)
|
||||
r.Close()
|
||||
|
||||
// If the destination is a directory, figure out the filename.
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
|
@ -515,7 +565,7 @@ func (b *buildFile) CmdAdd(args string) error {
|
|||
}
|
||||
defer container.Unmount()
|
||||
|
||||
if err := b.addContext(container, origPath, destPath); err != nil {
|
||||
if err := b.addContext(container, origPath, destPath, isRemote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -554,16 +604,16 @@ func (sf *StderrFormater) Write(buf []byte) (int, error) {
|
|||
return len(buf), err
|
||||
}
|
||||
|
||||
func (b *buildFile) run() (string, error) {
|
||||
func (b *buildFile) create() (*Container, error) {
|
||||
if b.image == "" {
|
||||
return "", fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
b.config.Image = b.image
|
||||
|
||||
// Create the container and start it
|
||||
c, _, err := b.runtime.Create(b.config, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
b.tmpContainers[c.ID] = struct{}{}
|
||||
fmt.Fprintf(b.outStream, " ---> Running in %s\n", utils.TruncateID(c.ID))
|
||||
|
@ -572,6 +622,10 @@ func (b *buildFile) run() (string, error) {
|
|||
c.Path = b.config.Cmd[0]
|
||||
c.Args = b.config.Cmd[1:]
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (b *buildFile) run(c *Container) error {
|
||||
var errCh chan error
|
||||
|
||||
if b.verbose {
|
||||
|
@ -582,12 +636,12 @@ func (b *buildFile) run() (string, error) {
|
|||
|
||||
//start the container
|
||||
if err := c.Start(); err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
if errCh != nil {
|
||||
if err := <-errCh; err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,10 +651,10 @@ func (b *buildFile) run() (string, error) {
|
|||
Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.config.Cmd, ret),
|
||||
Code: ret,
|
||||
}
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
return c.ID, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit the container <id> with the autorun command <autoCmd>
|
||||
|
@ -742,7 +796,7 @@ func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeC
|
|||
return &buildFile{
|
||||
runtime: srv.runtime,
|
||||
srv: srv,
|
||||
config: &Config{},
|
||||
config: &runconfig.Config{},
|
||||
outStream: outStream,
|
||||
errStream: errStream,
|
||||
tmpContainers: make(map[string]struct{}),
|
||||
|
|
40
builtins/builtins.go
Normal file
40
builtins/builtins.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package builtins
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/engine"
|
||||
|
||||
"github.com/dotcloud/docker"
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/networkdriver/lxc"
|
||||
)
|
||||
|
||||
func Register(eng *engine.Engine) {
|
||||
daemon(eng)
|
||||
remote(eng)
|
||||
}
|
||||
|
||||
// remote: a RESTful api for cross-docker communication
|
||||
func remote(eng *engine.Engine) {
|
||||
eng.Register("serveapi", api.ServeApi)
|
||||
}
|
||||
|
||||
// daemon: a default execution and storage backend for Docker on Linux,
|
||||
// with the following underlying components:
|
||||
//
|
||||
// * Pluggable storage drivers including aufs, vfs, lvm and btrfs.
|
||||
// * Pluggable execution drivers including lxc and chroot.
|
||||
//
|
||||
// In practice `daemon` still includes most core Docker components, including:
|
||||
//
|
||||
// * The reference registry client implementation
|
||||
// * Image management
|
||||
// * The build facility
|
||||
// * Logging
|
||||
//
|
||||
// These components should be broken off into plugins of their own.
|
||||
//
|
||||
func daemon(eng *engine.Engine) {
|
||||
eng.Register("initserver", docker.InitServer)
|
||||
eng.Register("init_networkdriver", lxc.InitDriver)
|
||||
eng.Register("version", docker.GetVersion)
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
|
||||
config, hostConfig, _, err := ParseRun(strings.Split(args+" ubuntu bash", " "), nil)
|
||||
func parse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig, error) {
|
||||
config, hostConfig, _, err := runconfig.Parse(strings.Split(args+" ubuntu bash", " "), nil)
|
||||
return config, hostConfig, err
|
||||
}
|
||||
|
||||
func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
|
||||
func mustParse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig) {
|
||||
config, hostConfig, err := parse(t, args)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -25,6 +25,7 @@ type DaemonConfig struct {
|
|||
BridgeIP string
|
||||
InterContainerCommunication bool
|
||||
GraphDriver string
|
||||
ExecDriver string
|
||||
Mtu int
|
||||
DisableNetwork bool
|
||||
}
|
||||
|
@ -43,6 +44,7 @@ func DaemonConfigFromJob(job *engine.Job) *DaemonConfig {
|
|||
DefaultIp: net.ParseIP(job.Getenv("DefaultIp")),
|
||||
InterContainerCommunication: job.GetenvBool("InterContainerCommunication"),
|
||||
GraphDriver: job.Getenv("GraphDriver"),
|
||||
ExecDriver: job.Getenv("ExecDriver"),
|
||||
}
|
||||
if dns := job.GetenvList("Dns"); dns != nil {
|
||||
config.Dns = dns
|
||||
|
|
651
container.go
651
container.go
|
@ -8,25 +8,28 @@ import (
|
|||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/execdriver"
|
||||
"github.com/dotcloud/docker/graphdriver"
|
||||
"github.com/dotcloud/docker/pkg/mount"
|
||||
"github.com/dotcloud/docker/pkg/term"
|
||||
"github.com/dotcloud/docker/links"
|
||||
"github.com/dotcloud/docker/nat"
|
||||
"github.com/dotcloud/docker/runconfig"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/kr/pty"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
var (
|
||||
ErrNotATTY = errors.New("The PTY is not a file")
|
||||
ErrNoTTY = errors.New("No PTY found")
|
||||
ErrNotATTY = errors.New("The PTY is not a file")
|
||||
ErrNoTTY = errors.New("No PTY found")
|
||||
ErrContainerStart = errors.New("The container failed to start. Unknown error")
|
||||
ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
|
@ -41,7 +44,7 @@ type Container struct {
|
|||
Path string
|
||||
Args []string
|
||||
|
||||
Config *Config
|
||||
Config *runconfig.Config
|
||||
State State
|
||||
Image string
|
||||
|
||||
|
@ -52,13 +55,13 @@ type Container struct {
|
|||
HostsPath string
|
||||
Name string
|
||||
Driver string
|
||||
ExecDriver string
|
||||
|
||||
command *execdriver.Command
|
||||
stdout *utils.WriteBroadcaster
|
||||
stderr *utils.WriteBroadcaster
|
||||
stdin io.ReadCloser
|
||||
stdinPipe io.WriteCloser
|
||||
ptyMaster io.Closer
|
||||
|
||||
runtime *Runtime
|
||||
|
||||
|
@ -67,160 +70,12 @@ type Container struct {
|
|||
// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
|
||||
// Easier than migrating older container configs :)
|
||||
VolumesRW map[string]bool
|
||||
hostConfig *HostConfig
|
||||
hostConfig *runconfig.HostConfig
|
||||
|
||||
activeLinks map[string]*Link
|
||||
}
|
||||
|
||||
// Note: the Config structure should hold only portable information about the container.
|
||||
// Here, "portable" means "independent from the host we are running on".
|
||||
// Non-portable information *should* appear in HostConfig.
|
||||
type Config struct {
|
||||
Hostname string
|
||||
Domainname string
|
||||
User string
|
||||
Memory int64 // Memory limit (in bytes)
|
||||
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
|
||||
CpuShares int64 // CPU shares (relative weight vs. other containers)
|
||||
AttachStdin bool
|
||||
AttachStdout bool
|
||||
AttachStderr bool
|
||||
PortSpecs []string // Deprecated - Can be in the format of 8080/tcp
|
||||
ExposedPorts map[Port]struct{}
|
||||
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||
OpenStdin bool // Open stdin
|
||||
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||
Env []string
|
||||
Cmd []string
|
||||
Dns []string
|
||||
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
|
||||
Volumes map[string]struct{}
|
||||
VolumesFrom string
|
||||
WorkingDir string
|
||||
Entrypoint []string
|
||||
NetworkDisabled bool
|
||||
OnBuild []string
|
||||
}
|
||||
|
||||
func ContainerConfigFromJob(job *engine.Job) *Config {
|
||||
config := &Config{
|
||||
Hostname: job.Getenv("Hostname"),
|
||||
Domainname: job.Getenv("Domainname"),
|
||||
User: job.Getenv("User"),
|
||||
Memory: job.GetenvInt64("Memory"),
|
||||
MemorySwap: job.GetenvInt64("MemorySwap"),
|
||||
CpuShares: job.GetenvInt64("CpuShares"),
|
||||
AttachStdin: job.GetenvBool("AttachStdin"),
|
||||
AttachStdout: job.GetenvBool("AttachStdout"),
|
||||
AttachStderr: job.GetenvBool("AttachStderr"),
|
||||
Tty: job.GetenvBool("Tty"),
|
||||
OpenStdin: job.GetenvBool("OpenStdin"),
|
||||
StdinOnce: job.GetenvBool("StdinOnce"),
|
||||
Image: job.Getenv("Image"),
|
||||
VolumesFrom: job.Getenv("VolumesFrom"),
|
||||
WorkingDir: job.Getenv("WorkingDir"),
|
||||
NetworkDisabled: job.GetenvBool("NetworkDisabled"),
|
||||
}
|
||||
job.GetenvJson("ExposedPorts", &config.ExposedPorts)
|
||||
job.GetenvJson("Volumes", &config.Volumes)
|
||||
if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
|
||||
config.PortSpecs = PortSpecs
|
||||
}
|
||||
if Env := job.GetenvList("Env"); Env != nil {
|
||||
config.Env = Env
|
||||
}
|
||||
if Cmd := job.GetenvList("Cmd"); Cmd != nil {
|
||||
config.Cmd = Cmd
|
||||
}
|
||||
if Dns := job.GetenvList("Dns"); Dns != nil {
|
||||
config.Dns = Dns
|
||||
}
|
||||
if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
|
||||
config.Entrypoint = Entrypoint
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type HostConfig struct {
|
||||
Binds []string
|
||||
ContainerIDFile string
|
||||
LxcConf []KeyValuePair
|
||||
Privileged bool
|
||||
PortBindings map[Port][]PortBinding
|
||||
Links []string
|
||||
PublishAllPorts bool
|
||||
}
|
||||
|
||||
func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
|
||||
hostConfig := &HostConfig{
|
||||
ContainerIDFile: job.Getenv("ContainerIDFile"),
|
||||
Privileged: job.GetenvBool("Privileged"),
|
||||
PublishAllPorts: job.GetenvBool("PublishAllPorts"),
|
||||
}
|
||||
job.GetenvJson("LxcConf", &hostConfig.LxcConf)
|
||||
job.GetenvJson("PortBindings", &hostConfig.PortBindings)
|
||||
if Binds := job.GetenvList("Binds"); Binds != nil {
|
||||
hostConfig.Binds = Binds
|
||||
}
|
||||
if Links := job.GetenvList("Links"); Links != nil {
|
||||
hostConfig.Links = Links
|
||||
}
|
||||
|
||||
return hostConfig
|
||||
}
|
||||
|
||||
type BindMap struct {
|
||||
SrcPath string
|
||||
DstPath string
|
||||
Mode string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrContainerStart = errors.New("The container failed to start. Unknown error")
|
||||
ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
|
||||
ErrInvalidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
|
||||
ErrConflictAttachDetach = errors.New("Conflicting options: -a and -d")
|
||||
ErrConflictDetachAutoRemove = errors.New("Conflicting options: -rm and -d")
|
||||
)
|
||||
|
||||
type KeyValuePair struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type PortBinding struct {
|
||||
HostIp string
|
||||
HostPort string
|
||||
}
|
||||
|
||||
// 80/tcp
|
||||
type Port string
|
||||
|
||||
func (p Port) Proto() string {
|
||||
parts := strings.Split(string(p), "/")
|
||||
if len(parts) == 1 {
|
||||
return "tcp"
|
||||
}
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
func (p Port) Port() string {
|
||||
return strings.Split(string(p), "/")[0]
|
||||
}
|
||||
|
||||
func (p Port) Int() int {
|
||||
i, err := parsePort(p.Port())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func NewPort(proto, port string) Port {
|
||||
return Port(fmt.Sprintf("%s/%s", port, proto))
|
||||
activeLinks map[string]*links.Link
|
||||
}
|
||||
|
||||
// FIXME: move deprecated port stuff to nat to clean up the core.
|
||||
type PortMapping map[string]string // Deprecated
|
||||
|
||||
type NetworkSettings struct {
|
||||
|
@ -229,13 +84,13 @@ type NetworkSettings struct {
|
|||
Gateway string
|
||||
Bridge string
|
||||
PortMapping map[string]PortMapping // Deprecated
|
||||
Ports map[Port][]PortBinding
|
||||
Ports nat.PortMap
|
||||
}
|
||||
|
||||
func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
|
||||
var outs = engine.NewTable("", 0)
|
||||
for port, bindings := range settings.Ports {
|
||||
p, _ := parsePort(port.Port())
|
||||
p, _ := nat.ParsePort(port.Port())
|
||||
if len(bindings) == 0 {
|
||||
out := &engine.Env{}
|
||||
out.SetInt("PublicPort", p)
|
||||
|
@ -245,7 +100,7 @@ func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
|
|||
}
|
||||
for _, binding := range bindings {
|
||||
out := &engine.Env{}
|
||||
h, _ := parsePort(binding.HostPort)
|
||||
h, _ := nat.ParsePort(binding.HostPort)
|
||||
out.SetInt("PrivatePort", p)
|
||||
out.SetInt("PublicPort", h)
|
||||
out.Set("Type", port.Proto())
|
||||
|
@ -322,7 +177,7 @@ func (container *Container) ToDisk() (err error) {
|
|||
}
|
||||
|
||||
func (container *Container) readHostConfig() error {
|
||||
container.hostConfig = &HostConfig{}
|
||||
container.hostConfig = &runconfig.HostConfig{}
|
||||
// If the hostconfig file does not exist, do not read it.
|
||||
// (We still have to initialize container.hostConfig,
|
||||
// but that's OK, since we just did that above.)
|
||||
|
@ -358,55 +213,6 @@ func (container *Container) generateEnvConfig(env []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) setupPty() error {
|
||||
ptyMaster, ptySlave, err := pty.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.ptyMaster = ptyMaster
|
||||
container.command.Stdout = ptySlave
|
||||
container.command.Stderr = ptySlave
|
||||
|
||||
// Copy the PTYs to our broadcasters
|
||||
go func() {
|
||||
defer container.stdout.CloseWriters()
|
||||
utils.Debugf("startPty: begin of stdout pipe")
|
||||
io.Copy(container.stdout, ptyMaster)
|
||||
utils.Debugf("startPty: end of stdout pipe")
|
||||
}()
|
||||
|
||||
// stdin
|
||||
if container.Config.OpenStdin {
|
||||
container.command.Stdin = ptySlave
|
||||
container.command.SysProcAttr.Setctty = true
|
||||
go func() {
|
||||
defer container.stdin.Close()
|
||||
utils.Debugf("startPty: begin of stdin pipe")
|
||||
io.Copy(ptyMaster, container.stdin)
|
||||
utils.Debugf("startPty: end of stdin pipe")
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) setupStd() error {
|
||||
container.command.Stdout = container.stdout
|
||||
container.command.Stderr = container.stderr
|
||||
if container.Config.OpenStdin {
|
||||
stdin, err := container.command.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
defer stdin.Close()
|
||||
utils.Debugf("start: begin of stdin pipe")
|
||||
io.Copy(stdin, container.stdin)
|
||||
utils.Debugf("start: end of stdin pipe")
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
|
||||
var cStdout, cStderr io.ReadCloser
|
||||
|
||||
|
@ -637,24 +443,14 @@ func (container *Container) Start() (err error) {
|
|||
log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
|
||||
}
|
||||
|
||||
if container.Volumes == nil || len(container.Volumes) == 0 {
|
||||
container.Volumes = make(map[string]string)
|
||||
container.VolumesRW = make(map[string]bool)
|
||||
}
|
||||
|
||||
// Apply volumes from another container if requested
|
||||
if err := container.applyExternalVolumes(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := container.createVolumes(); err != nil {
|
||||
if err := prepareVolumesForContainer(container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup environment
|
||||
env := []string{
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"PATH=" + defaultPathEnv,
|
||||
"HOSTNAME=" + container.Config.Hostname,
|
||||
}
|
||||
|
||||
|
@ -671,7 +467,7 @@ func (container *Container) Start() (err error) {
|
|||
}
|
||||
|
||||
if len(children) > 0 {
|
||||
container.activeLinks = make(map[string]*Link, len(children))
|
||||
container.activeLinks = make(map[string]*links.Link, len(children))
|
||||
|
||||
// If we encounter an error make sure that we rollback any network
|
||||
// config and ip table changes
|
||||
|
@ -682,8 +478,19 @@ func (container *Container) Start() (err error) {
|
|||
container.activeLinks = nil
|
||||
}
|
||||
|
||||
for p, child := range children {
|
||||
link, err := NewLink(container, child, p, runtime.eng)
|
||||
for linkAlias, child := range children {
|
||||
if !child.State.IsRunning() {
|
||||
return fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
|
||||
}
|
||||
|
||||
link, err := links.NewLink(
|
||||
container.NetworkSettings.IPAddress,
|
||||
child.NetworkSettings.IPAddress,
|
||||
linkAlias,
|
||||
child.Config.Env,
|
||||
child.Config.ExposedPorts,
|
||||
runtime.eng)
|
||||
|
||||
if err != nil {
|
||||
rollback()
|
||||
return err
|
||||
|
@ -701,10 +508,10 @@ func (container *Container) Start() (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
for _, elem := range container.Config.Env {
|
||||
env = append(env, elem)
|
||||
}
|
||||
|
||||
// because the env on the container can override certain default values
|
||||
// we need to replace the 'env' keys where they match and append anything
|
||||
// else.
|
||||
env = utils.ReplaceOrAppendEnvValues(env, container.Config.Env)
|
||||
if err := container.generateEnvConfig(env); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -721,63 +528,12 @@ func (container *Container) Start() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// Setup the root fs as a bind mount of the base fs
|
||||
root := container.RootfsPath()
|
||||
if err := os.MkdirAll(root, 0755); err != nil && !os.IsExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a bind mount of the base fs as a place where we can add mounts
|
||||
// without affecting the ability to access the base fs
|
||||
if err := mount.Mount(container.basefs, root, "none", "bind,rw"); err != nil {
|
||||
if err := mountVolumesForContainer(container, envPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure the root fs is private so the mounts here don't propagate to basefs
|
||||
if err := mount.ForceMount(root, root, "none", "private"); 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[r] {
|
||||
mountAs = "rw"
|
||||
}
|
||||
|
||||
r = path.Join(root, r)
|
||||
if p, err := utils.FollowSymlinkInScope(r, root); err != nil {
|
||||
return err
|
||||
} else {
|
||||
r = p
|
||||
}
|
||||
|
||||
if err := mount.Mount(v, r, "none", fmt.Sprintf("bind,%s", mountAs)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
populateCommand(container)
|
||||
container.command.Env = env
|
||||
|
||||
// Setup logging of stdout and stderr to disk
|
||||
if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
|
||||
|
@ -788,17 +544,6 @@ func (container *Container) Start() (err error) {
|
|||
}
|
||||
container.waitLock = make(chan struct{})
|
||||
|
||||
// Setuping pipes and/or Pty
|
||||
var setup func() error
|
||||
if container.Config.Tty {
|
||||
setup = container.setupPty
|
||||
} else {
|
||||
setup = container.setupStd
|
||||
}
|
||||
if err := setup(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
callbackLock := make(chan struct{})
|
||||
callback := func(command *execdriver.Command) {
|
||||
container.State.SetRunning(command.Pid())
|
||||
|
@ -829,205 +574,6 @@ func (container *Container) Start() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) getBindMap() (map[string]BindMap, error) {
|
||||
// Create the requested bind mounts
|
||||
binds := make(map[string]BindMap)
|
||||
// Define illegal container destinations
|
||||
illegalDsts := []string{"/", "."}
|
||||
|
||||
for _, bind := range container.hostConfig.Binds {
|
||||
// FIXME: factorize bind parsing in parseBind
|
||||
var src, dst, mode string
|
||||
arr := strings.Split(bind, ":")
|
||||
if len(arr) == 2 {
|
||||
src = arr[0]
|
||||
dst = arr[1]
|
||||
mode = "rw"
|
||||
} else if len(arr) == 3 {
|
||||
src = arr[0]
|
||||
dst = arr[1]
|
||||
mode = arr[2]
|
||||
} else {
|
||||
return nil, fmt.Errorf("Invalid bind specification: %s", bind)
|
||||
}
|
||||
|
||||
// Bail if trying to mount to an illegal destination
|
||||
for _, illegal := range illegalDsts {
|
||||
if dst == illegal {
|
||||
return nil, fmt.Errorf("Illegal bind destination: %s", dst)
|
||||
}
|
||||
}
|
||||
|
||||
bindMap := BindMap{
|
||||
SrcPath: src,
|
||||
DstPath: dst,
|
||||
Mode: mode,
|
||||
}
|
||||
binds[path.Clean(dst)] = bindMap
|
||||
}
|
||||
return binds, nil
|
||||
}
|
||||
|
||||
func (container *Container) createVolumes() error {
|
||||
binds, err := container.getBindMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
volumesDriver := container.runtime.volumes.driver
|
||||
// Create the requested volumes if they don't exist
|
||||
for volPath := range container.Config.Volumes {
|
||||
volPath = path.Clean(volPath)
|
||||
volIsDir := true
|
||||
// Skip existing volumes
|
||||
if _, exists := container.Volumes[volPath]; exists {
|
||||
continue
|
||||
}
|
||||
var srcPath string
|
||||
var isBindMount bool
|
||||
srcRW := false
|
||||
// If an external bind is defined for this volume, use that as a source
|
||||
if bindMap, exists := binds[volPath]; exists {
|
||||
isBindMount = true
|
||||
srcPath = bindMap.SrcPath
|
||||
if strings.ToLower(bindMap.Mode) == "rw" {
|
||||
srcRW = true
|
||||
}
|
||||
if stat, err := os.Stat(bindMap.SrcPath); err != nil {
|
||||
return err
|
||||
} else {
|
||||
volIsDir = stat.IsDir()
|
||||
}
|
||||
// Otherwise create an directory in $ROOT/volumes/ and use that
|
||||
} else {
|
||||
|
||||
// Do not pass a container as the parameter for the volume creation.
|
||||
// The graph driver using the container's information ( Image ) to
|
||||
// create the parent.
|
||||
c, err := container.runtime.volumes.Create(nil, nil, "", "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcPath, err = volumesDriver.Get(c.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
|
||||
}
|
||||
srcRW = true // RW by default
|
||||
}
|
||||
|
||||
if p, err := filepath.EvalSymlinks(srcPath); err != nil {
|
||||
return err
|
||||
} else {
|
||||
srcPath = p
|
||||
}
|
||||
|
||||
container.Volumes[volPath] = srcPath
|
||||
container.VolumesRW[volPath] = srcRW
|
||||
|
||||
// Create the mountpoint
|
||||
volPath = path.Join(container.basefs, volPath)
|
||||
rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.basefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(rootVolPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if volIsDir {
|
||||
if err := os.MkdirAll(rootVolPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := os.MkdirAll(path.Dir(rootVolPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if f, err := os.OpenFile(rootVolPath, os.O_CREATE, 0755); err != nil {
|
||||
return err
|
||||
} else {
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not copy or change permissions if we are mounting from the host
|
||||
if srcRW && !isBindMount {
|
||||
volList, err := ioutil.ReadDir(rootVolPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(volList) > 0 {
|
||||
srcList, err := ioutil.ReadDir(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(srcList) == 0 {
|
||||
// If the source volume is empty copy files from the root into the volume
|
||||
if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var stat syscall.Stat_t
|
||||
if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
||||
return err
|
||||
}
|
||||
var srcStat syscall.Stat_t
|
||||
if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
||||
return err
|
||||
}
|
||||
// Change the source volume's ownership if it differs from the root
|
||||
// files that were just copied
|
||||
if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
|
||||
if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) applyExternalVolumes() error {
|
||||
if container.Config.VolumesFrom != "" {
|
||||
containerSpecs := strings.Split(container.Config.VolumesFrom, ",")
|
||||
for _, containerSpec := range containerSpecs {
|
||||
mountRW := true
|
||||
specParts := strings.SplitN(containerSpec, ":", 2)
|
||||
switch len(specParts) {
|
||||
case 0:
|
||||
return fmt.Errorf("Malformed volumes-from specification: %s", container.Config.VolumesFrom)
|
||||
case 2:
|
||||
switch specParts[1] {
|
||||
case "ro":
|
||||
mountRW = false
|
||||
case "rw": // mountRW is already true
|
||||
default:
|
||||
return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
|
||||
}
|
||||
}
|
||||
c := container.runtime.Get(specParts[0])
|
||||
if c == nil {
|
||||
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
|
||||
}
|
||||
for volPath, id := range c.Volumes {
|
||||
if _, exists := container.Volumes[volPath]; exists {
|
||||
continue
|
||||
}
|
||||
if err := os.MkdirAll(path.Join(container.basefs, volPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
container.Volumes[volPath] = id
|
||||
if isRW, exists := c.VolumesRW[volPath]; exists {
|
||||
container.VolumesRW[volPath] = isRW && mountRW
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) Run() error {
|
||||
if err := container.Start(); err != nil {
|
||||
return err
|
||||
|
@ -1152,8 +698,8 @@ func (container *Container) allocateNetwork() error {
|
|||
}
|
||||
|
||||
var (
|
||||
portSpecs = make(map[Port]struct{})
|
||||
bindings = make(map[Port][]PortBinding)
|
||||
portSpecs = make(nat.PortSet)
|
||||
bindings = make(nat.PortMap)
|
||||
)
|
||||
|
||||
if !container.State.IsGhost() {
|
||||
|
@ -1177,7 +723,7 @@ func (container *Container) allocateNetwork() error {
|
|||
for port := range portSpecs {
|
||||
binding := bindings[port]
|
||||
if container.hostConfig.PublishAllPorts && len(binding) == 0 {
|
||||
binding = append(binding, PortBinding{})
|
||||
binding = append(binding, nat.PortBinding{})
|
||||
}
|
||||
|
||||
for i := 0; i < len(binding); i++ {
|
||||
|
@ -1232,18 +778,27 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
|
|||
exitCode int
|
||||
)
|
||||
|
||||
if container.command == nil {
|
||||
// This happends when you have a GHOST container with lxc
|
||||
populateCommand(container)
|
||||
err = container.runtime.RestoreCommand(container)
|
||||
} else {
|
||||
exitCode, err = container.runtime.Run(container, callback)
|
||||
}
|
||||
|
||||
pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin)
|
||||
exitCode, err = container.runtime.Run(container, pipes, callback)
|
||||
if err != nil {
|
||||
utils.Errorf("Error running container: %s", err)
|
||||
}
|
||||
|
||||
if container.runtime.srv.IsRunning() {
|
||||
container.State.SetStopped(exitCode)
|
||||
|
||||
// FIXME: there is a race condition here which causes this to fail during the unit tests.
|
||||
// If another goroutine was waiting for Wait() to return before removing the container's root
|
||||
// from the filesystem... At this point it may already have done so.
|
||||
// This is because State.setStopped() has already been called, and has caused Wait()
|
||||
// to return.
|
||||
// FIXME: why are we serializing running state to disk in the first place?
|
||||
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
||||
if err := container.ToDisk(); err != nil {
|
||||
utils.Errorf("Error dumping container state to disk: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
container.cleanup()
|
||||
|
||||
|
@ -1252,23 +807,12 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
|
|||
container.stdin, container.stdinPipe = io.Pipe()
|
||||
}
|
||||
|
||||
container.State.SetStopped(exitCode)
|
||||
|
||||
if container.runtime != nil && container.runtime.srv != nil {
|
||||
container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
|
||||
}
|
||||
|
||||
close(container.waitLock)
|
||||
|
||||
// FIXME: there is a race condition here which causes this to fail during the unit tests.
|
||||
// If another goroutine was waiting for Wait() to return before removing the container's root
|
||||
// from the filesystem... At this point it may already have done so.
|
||||
// This is because State.setStopped() has already been called, and has caused Wait()
|
||||
// to return.
|
||||
// FIXME: why are we serializing running state to disk in the first place?
|
||||
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
|
||||
container.ToDisk()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1281,7 +825,6 @@ func (container *Container) cleanup() {
|
|||
link.Disable()
|
||||
}
|
||||
}
|
||||
|
||||
if container.Config.OpenStdin {
|
||||
if err := container.stdin.Close(); err != nil {
|
||||
utils.Errorf("%s: Error close stdin: %s", container.ID, err)
|
||||
|
@ -1293,36 +836,13 @@ func (container *Container) cleanup() {
|
|||
if err := container.stderr.CloseWriters(); err != nil {
|
||||
utils.Errorf("%s: Error close stderr: %s", container.ID, err)
|
||||
}
|
||||
|
||||
if container.ptyMaster != nil {
|
||||
if err := container.ptyMaster.Close(); err != nil {
|
||||
utils.Errorf("%s: Error closing Pty master: %s", container.ID, err)
|
||||
if container.command != nil && container.command.Terminal != nil {
|
||||
if err := container.command.Terminal.Close(); err != nil {
|
||||
utils.Errorf("%s: Error closing terminal: %s", container.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
root = container.RootfsPath()
|
||||
mounts = []string{
|
||||
root,
|
||||
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 i := len(mounts) - 1; i >= 0; i-- {
|
||||
if lastError := mount.Unmount(mounts[i]); lastError != nil {
|
||||
log.Printf("Failed to umount %v: %v", mounts[i], lastError)
|
||||
}
|
||||
}
|
||||
unmountVolumesForContainer(container)
|
||||
|
||||
if err := container.Unmount(); err != nil {
|
||||
log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
|
||||
|
@ -1390,6 +910,13 @@ func (container *Container) Stop(seconds int) error {
|
|||
}
|
||||
|
||||
func (container *Container) Restart(seconds int) error {
|
||||
// Avoid unnecessarily unmounting and then directly mounting
|
||||
// the container when the container stops and then starts
|
||||
// again
|
||||
if err := container.Mount(); err == nil {
|
||||
defer container.Unmount()
|
||||
}
|
||||
|
||||
if err := container.Stop(seconds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1403,11 +930,7 @@ func (container *Container) Wait() int {
|
|||
}
|
||||
|
||||
func (container *Container) Resize(h, w int) error {
|
||||
pty, ok := container.ptyMaster.(*os.File)
|
||||
if !ok {
|
||||
return fmt.Errorf("ptyMaster does not have Fd() method")
|
||||
}
|
||||
return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||
return container.command.Terminal.Resize(h, w)
|
||||
}
|
||||
|
||||
func (container *Container) ExportRw() (archive.Archive, error) {
|
||||
|
@ -1422,7 +945,11 @@ func (container *Container) ExportRw() (archive.Archive, error) {
|
|||
container.Unmount()
|
||||
return nil, err
|
||||
}
|
||||
return EofReader(archive, func() { container.Unmount() }), nil
|
||||
return utils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
container.Unmount()
|
||||
return err
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (container *Container) Export() (archive.Archive, error) {
|
||||
|
@ -1435,7 +962,11 @@ func (container *Container) Export() (archive.Archive, error) {
|
|||
container.Unmount()
|
||||
return nil, err
|
||||
}
|
||||
return EofReader(archive, func() { container.Unmount() }), nil
|
||||
return utils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
container.Unmount()
|
||||
return err
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (container *Container) WaitTimeout(timeout time.Duration) error {
|
||||
|
@ -1562,7 +1093,7 @@ func (container *Container) GetSize() (int64, int64) {
|
|||
return sizeRw, sizeRootfs
|
||||
}
|
||||
|
||||
func (container *Container) Copy(resource string) (archive.Archive, error) {
|
||||
func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
||||
if err := container.Mount(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1589,21 +1120,23 @@ func (container *Container) Copy(resource string) (archive.Archive, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return EofReader(archive, func() { container.Unmount() }), nil
|
||||
return utils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
container.Unmount()
|
||||
return err
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Returns true if the container exposes a certain port
|
||||
func (container *Container) Exposes(p Port) bool {
|
||||
func (container *Container) Exposes(p nat.Port) bool {
|
||||
_, exists := container.Config.ExposedPorts[p]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (container *Container) GetPtyMaster() (*os.File, error) {
|
||||
if container.ptyMaster == nil {
|
||||
ttyConsole, ok := container.command.Terminal.(execdriver.TtyTerminal)
|
||||
if !ok {
|
||||
return nil, ErrNoTTY
|
||||
}
|
||||
if pty, ok := container.ptyMaster.(*os.File); ok {
|
||||
return pty, nil
|
||||
}
|
||||
return nil, ErrNotATTY
|
||||
return ttyConsole.Master(), nil
|
||||
}
|
||||
|
|
|
@ -1,28 +1,12 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"github.com/dotcloud/docker/nat"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseLxcConfOpt(t *testing.T) {
|
||||
opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
|
||||
|
||||
for _, o := range opts {
|
||||
k, v, err := parseLxcOpt(o)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
if k != "lxc.utsname" {
|
||||
t.Fail()
|
||||
}
|
||||
if v != "docker" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
||||
ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::80"})
|
||||
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -64,7 +48,7 @@ func TestParseNetworkOptsPrivateOnly(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParseNetworkOptsPublic(t *testing.T) {
|
||||
ports, bindings, err := parsePortSpecs([]string{"192.168.1.100:8080:80"})
|
||||
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100:8080:80"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -106,7 +90,7 @@ func TestParseNetworkOptsPublic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParseNetworkOptsUdp(t *testing.T) {
|
||||
ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::6000/udp"})
|
||||
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::6000/udp"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
257
contrib/completion/fish/docker.fish
Normal file
257
contrib/completion/fish/docker.fish
Normal file
|
@ -0,0 +1,257 @@
|
|||
# docker.fish - docker completions for fish shell
|
||||
#
|
||||
# This file is generated by gen_docker_fish_completions.py from:
|
||||
# https://github.com/barnybug/docker-fish-completion
|
||||
#
|
||||
# To install the completions:
|
||||
# mkdir -p ~/.config/fish/completions
|
||||
# cp docker.fish ~/.config/fish/completions
|
||||
#
|
||||
# Completion supported:
|
||||
# - parameters
|
||||
# - commands
|
||||
# - containers
|
||||
# - images
|
||||
# - repositories
|
||||
|
||||
function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i attach build commit cp diff events export history images import info insert inspect kill load login logs port ps pull push restart rm rmi run save search start stop tag top version wait
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_print_docker_containers --description 'Print a list of docker containers' -a select
|
||||
switch $select
|
||||
case running
|
||||
docker ps -a --no-trunc | awk 'NR>1' | awk 'BEGIN {FS=" +"}; $5 ~ "^Up" {print $1 "\n" $(NF-1)}' | tr ',' '\n'
|
||||
case stopped
|
||||
docker ps -a --no-trunc | awk 'NR>1' | awk 'BEGIN {FS=" +"}; $5 ~ "^Exit" {print $1 "\n" $(NF-1)}' | tr ',' '\n'
|
||||
case all
|
||||
docker ps -a --no-trunc | awk 'NR>1' | awk 'BEGIN {FS=" +"}; {print $1 "\n" $(NF-1)}' | tr ',' '\n'
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_print_docker_images --description 'Print a list of docker images'
|
||||
docker images | awk 'NR>1' | grep -v '<none>' | awk '{print $1":"$2}'
|
||||
end
|
||||
|
||||
function __fish_print_docker_repositories --description 'Print a list of docker repositories'
|
||||
docker images | awk 'NR>1' | grep -v '<none>' | awk '{print $1}' | sort | uniq
|
||||
end
|
||||
|
||||
# common options
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s D -l debug -d 'Enable debug mode'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s H -l host -d 'tcp://host:port, unix://path/to/socket, fd://* or fd://socketfd to use in daemon mode. Multiple sockets can be specified'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l api-enable-cors -d 'Enable CORS headers in the remote API'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s b -l bridge -d "Attach containers to a pre-existing network bridge; use 'none' to disable container networking"
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l bip -d "Use this CIDR notation address for the network bridge's IP, not compatible with -b"
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s d -l daemon -d 'Enable daemon mode'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l dns -d 'Force docker to use specific DNS servers'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s g -l graph -d 'Path to use as the root of the docker runtime'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l icc -d 'Enable inter-container communication'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip -d 'Default IP address to use when binding container ports'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l ip-forward -d 'Disable enabling of net.ipv4.ip_forward'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l iptables -d "Disable docker's addition of iptables rules"
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -l mtu -d 'Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if not default route is available'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s p -l pidfile -d 'Path to use for daemon PID file'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s r -l restart -d 'Restart previously running containers'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s s -l storage-driver -d 'Force the docker runtime to use a specific storage driver'
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -s v -l version -d 'Print version information and quit'
|
||||
|
||||
# subcommands
|
||||
# attach
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a attach -d 'Attach to a running container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l no-stdin -d 'Do not attach stdin'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -l sig-proxy -d 'Proxify all received signal to the process (even in non-tty mode)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from attach' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# build
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a build -d 'Build a container from a Dockerfile'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l no-cache -d 'Do not use cache when building the image'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s q -l quiet -d 'Suppress verbose build output'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l rm -d 'Remove intermediate containers after a successful build'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s t -l tag -d 'Repository name (and optionally a tag) to be applied to the resulting image in case of success'
|
||||
|
||||
# commit
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a commit -d "Create a new image from a container's changes"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s a -l author -d 'Author (eg. "John Hannibal Smith <hannibal@a-team.com>"'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -s m -l message -d 'Commit message'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -l run -d 'Config automatically applied when the image is run. (ex: -run=\'{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}\')'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||
|
||||
# cp
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a cp -d 'Copy files/folders from the containers filesystem to the host path'
|
||||
|
||||
# diff
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a diff -d "Inspect changes on a container's filesystem"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from diff' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||
|
||||
# events
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a events -d 'Get real time events from the server'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from events' -l since -d 'Show previously created events and then stream.'
|
||||
|
||||
# export
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a export -d 'Stream the contents of a container as a tar archive'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from export' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||
|
||||
# history
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a history -d 'Show the history of an image'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -l no-trunc -d "Don't truncate output"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -s q -l quiet -d 'only show numeric IDs'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from history' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# images
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a images -d 'List images'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s a -l all -d 'show all images (by default filter out the intermediate images used to build)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -l no-trunc -d "Don't truncate output"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s q -l quiet -d 'only show numeric IDs'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s t -l tree -d 'output graph in tree format'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -s v -l viz -d 'output graph in graphviz format'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from images' -a '(__fish_print_docker_repositories)' -d "Repository"
|
||||
|
||||
# import
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a import -d 'Create a new filesystem image from the contents of a tarball'
|
||||
|
||||
# info
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a info -d 'Display system-wide information'
|
||||
|
||||
# insert
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a insert -d 'Insert a file in an image'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from insert' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# inspect
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a inspect -d 'Return low-level information on a container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -s f -l format -d 'Format the output using the given go template.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_images)' -d "Image"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from inspect' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# kill
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a kill -d 'Kill a running container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from kill' -s s -l signal -d 'Signal to send to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from kill' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# load
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a load -d 'Load an image from a tar archive'
|
||||
|
||||
# login
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Register or Login to the docker registry server'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s e -l email -d 'email'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'password'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'username'
|
||||
|
||||
# logs
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a logs -d 'Fetch the logs of a container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -s f -l follow -d 'Follow log output'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# port
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a port -d 'Lookup the public-facing port which is NAT-ed to PRIVATE_PORT'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from port' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# ps
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a ps -d 'List containers'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s a -l all -d 'Show all containers. Only running containers are shown by default.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l before-id -d 'Show only container created before Id, include non-running ones.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s l -l latest -d 'Show only the latest created container, include non-running ones.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s n -d 'Show n last created containers, include non-running ones.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l no-trunc -d "Don't truncate output"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s q -l quiet -d 'Only display numeric IDs'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -s s -l size -d 'Display sizes'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from ps' -l since-id -d 'Show only containers created since Id, include non-running ones.'
|
||||
|
||||
# pull
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a pull -d 'Pull an image or a repository from the docker registry server'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -s t -l tag -d 'Download tagged image in repository'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -a '(__fish_print_docker_images)' -d "Image"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from pull' -a '(__fish_print_docker_repositories)' -d "Repository"
|
||||
|
||||
# push
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a push -d 'Push an image or a repository to the docker registry server'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from push' -a '(__fish_print_docker_images)' -d "Image"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from push' -a '(__fish_print_docker_repositories)' -d "Repository"
|
||||
|
||||
# restart
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a restart -d 'Restart a running container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from restart' -s t -l time -d 'Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from restart' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# rm
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a rm -d 'Remove one or more containers'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s l -l link -d 'Remove the specified link and not the underlying container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s v -l volumes -d 'Remove the volumes associated to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
||||
|
||||
# rmi
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a rmi -d 'Remove one or more images'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rmi' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# run
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a run -d 'Run a command in a new container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to the host interfaces'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s a -l attach -d 'Attach to stdin, stdout or stderr.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s c -l cpu-shares -d 'CPU shares (relative weight)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cidfile -d 'Write the container ID to the file'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s d -l detach -d 'Detached mode: Run container in the background, print new container id'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l dns -d 'Set custom dns servers'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s e -l env -d 'Set environment variables'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l entrypoint -d 'Overwrite the default entrypoint of the image'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l expose -d 'Expose a port from the container without publishing it to your host'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s h -l hostname -d 'Container host name'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s i -l interactive -d 'Keep stdin open even if not attached'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container (name:alias)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d 'Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s n -l networking -d 'Enable networking for this container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s p -l publish -d "Publish a container's port to the host (format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort) (use 'docker port' to see the actual mapping)"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l privileged -d 'Give extended privileges to this container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l rm -d 'Automatically remove the container when it exits (incompatible with -d)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'Proxify all received signal to the process (even in non-tty mode)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-tty'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# save
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a save -d 'Save an image to a tar archive'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from save' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# search
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a search -d 'Search for an image in the docker index'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -l no-trunc -d "Don't truncate output"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -s s -l stars -d 'Only displays with at least xxx stars'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from search' -s t -l trusted -d 'Only show trusted builds'
|
||||
|
||||
# start
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a start -d 'Start a stopped container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -s a -l attach -d "Attach container's stdout/stderr and forward all signals to the process"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -s i -l interactive -d "Attach container's stdin"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from start' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
||||
|
||||
# stop
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a stop -d 'Stop a running container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from stop' -s t -l time -d 'Number of seconds to wait for the container to stop before killing it.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from stop' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# tag
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a tag -d 'Tag an image into a repository'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from tag' -s f -l force -d 'Force'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from tag' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
# top
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a top -d 'Lookup the running processes of a container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from top' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# version
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a version -d 'Show the docker version information'
|
||||
|
||||
# wait
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a wait -d 'Block until a container stops, then print its exit code'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
|
|
@ -8,4 +8,4 @@ Examples
|
|||
========
|
||||
|
||||
* Data container: ./data/Dockerfile creates a data image sharing /data volume
|
||||
* Firefox: ./firefox/Dockerfile shows a way to dockerize a common multimedia application
|
||||
* Iceweasel: ./iceweasel/Dockerfile shows a way to dockerize a common multimedia application
|
|
@ -11,28 +11,28 @@
|
|||
# # Build data image
|
||||
# docker build -t data -rm .
|
||||
#
|
||||
# # Create a data container. (eg: firefox-data)
|
||||
# docker run -name firefox-data data true
|
||||
# # Create a data container. (eg: iceweasel-data)
|
||||
# docker run -name iceweasel-data data true
|
||||
#
|
||||
# # List data from it
|
||||
# docker run -volumes-from firefox-data busybox ls -al /data
|
||||
# docker run -volumes-from iceweasel-data busybox ls -al /data
|
||||
|
||||
docker-version 0.6.5
|
||||
|
||||
# Smallest base image, just to launch a container
|
||||
from busybox
|
||||
maintainer Daniel Mizyrycki <daniel@docker.com>
|
||||
FROM busybox
|
||||
MAINTAINER Daniel Mizyrycki <daniel@docker.com>
|
||||
|
||||
# Create a regular user
|
||||
run echo 'sysadmin:x:1000:1000::/data:/bin/sh' >> /etc/passwd
|
||||
run echo 'sysadmin:x:1000:' >> /etc/group
|
||||
RUN echo 'sysadmin:x:1000:1000::/data:/bin/sh' >> /etc/passwd
|
||||
RUN echo 'sysadmin:x:1000:' >> /etc/group
|
||||
|
||||
# Create directory for that user
|
||||
run mkdir /data
|
||||
run chown sysadmin.sysadmin /data
|
||||
RUN mkdir /data
|
||||
RUN chown sysadmin.sysadmin /data
|
||||
|
||||
# Add content to /data. This will keep sysadmin ownership
|
||||
run touch /data/init_volume
|
||||
RUN touch /data/init_volume
|
||||
|
||||
# Create /data volume
|
||||
VOLUME /data
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
# VERSION: 0.7
|
||||
# DESCRIPTION: Create firefox container with its dependencies
|
||||
# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
# COMMENTS:
|
||||
# This file describes how to build a Firefox container with all
|
||||
# dependencies installed. It uses native X11 unix socket and alsa
|
||||
# sound devices. Tested on Debian 7.2
|
||||
# USAGE:
|
||||
# # Download Firefox Dockerfile
|
||||
# wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/firefox/Dockerfile
|
||||
#
|
||||
# # Build firefox image
|
||||
# docker build -t firefox -rm .
|
||||
#
|
||||
# # Run stateful data-on-host firefox. For ephemeral, remove -v /data/firefox:/data
|
||||
# docker run -v /data/firefox:/data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY firefox
|
||||
#
|
||||
# # To run stateful dockerized data containers
|
||||
# docker run -volumes-from firefox-data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY firefox
|
||||
|
||||
docker-version 0.6.5
|
||||
|
||||
# Base docker image
|
||||
from tianon/debian:wheezy
|
||||
maintainer Daniel Mizyrycki <daniel@docker.com>
|
||||
|
||||
# Install firefox dependencies
|
||||
run echo "deb http://ftp.debian.org/debian/ wheezy main contrib" > /etc/apt/sources.list
|
||||
run apt-get update
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y libXrender1 libasound2 \
|
||||
libdbus-glib-1-2 libgtk2.0-0 libpango1.0-0 libxt6 wget bzip2 sudo
|
||||
|
||||
# Install Firefox
|
||||
run mkdir /application
|
||||
run cd /application; wget -O - \
|
||||
http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/25.0/linux-x86_64/en-US/firefox-25.0.tar.bz2 | tar jx
|
||||
|
||||
# create sysadmin account
|
||||
run useradd -m -d /data -p saIVpsc0EVTwA sysadmin
|
||||
run sed -Ei 's/sudo:x:27:/sudo:x:27:sysadmin/' /etc/group
|
||||
run sed -Ei 's/(\%sudo\s+ALL=\(ALL\:ALL\) )ALL/\1 NOPASSWD:ALL/' /etc/sudoers
|
||||
|
||||
# Autorun firefox. -no-remote is necessary to create a new container, as firefox
|
||||
# appears to communicate with itself through X11.
|
||||
cmd ["/bin/sh", "-c", "/usr/bin/sudo -u sysadmin -H -E /application/firefox/firefox -no-remote"]
|
41
contrib/desktop-integration/iceweasel/Dockerfile
Normal file
41
contrib/desktop-integration/iceweasel/Dockerfile
Normal file
|
@ -0,0 +1,41 @@
|
|||
# VERSION: 0.7
|
||||
# DESCRIPTION: Create iceweasel container with its dependencies
|
||||
# AUTHOR: Daniel Mizyrycki <daniel@dotcloud.com>
|
||||
# COMMENTS:
|
||||
# This file describes how to build a Iceweasel container with all
|
||||
# dependencies installed. It uses native X11 unix socket and alsa
|
||||
# sound devices. Tested on Debian 7.2
|
||||
# USAGE:
|
||||
# # Download Iceweasel Dockerfile
|
||||
# wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/iceweasel/Dockerfile
|
||||
#
|
||||
# # Build iceweasel image
|
||||
# docker build -t iceweasel -rm .
|
||||
#
|
||||
# # Run stateful data-on-host iceweasel. For ephemeral, remove -v /data/iceweasel:/data
|
||||
# docker run -v /data/iceweasel:/data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY iceweasel
|
||||
#
|
||||
# # To run stateful dockerized data containers
|
||||
# docker run -volumes-from iceweasel-data -v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
# -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
|
||||
# -e DISPLAY=unix$DISPLAY iceweasel
|
||||
|
||||
docker-version 0.6.5
|
||||
|
||||
# Base docker image
|
||||
FROM debian:wheezy
|
||||
MAINTAINER Daniel Mizyrycki <daniel@docker.com>
|
||||
|
||||
# Install Iceweasel and "sudo"
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq iceweasel sudo
|
||||
|
||||
# create sysadmin account
|
||||
RUN useradd -m -d /data -p saIVpsc0EVTwA sysadmin
|
||||
RUN sed -Ei 's/sudo:x:27:/sudo:x:27:sysadmin/' /etc/group
|
||||
RUN sed -Ei 's/(\%sudo\s+ALL=\(ALL\:ALL\) )ALL/\1 NOPASSWD:ALL/' /etc/sudoers
|
||||
|
||||
# Autorun iceweasel. -no-remote is necessary to create a new container, as
|
||||
# iceweasel appears to communicate with itself through X11.
|
||||
CMD ["/usr/bin/sudo", "-u", "sysadmin", "-H", "-E", "/usr/bin/iceweasel", "-no-remote"]
|
|
@ -6,6 +6,8 @@ After=network.target
|
|||
[Service]
|
||||
ExecStart=/usr/bin/docker -d
|
||||
Restart=on-failure
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -6,6 +6,8 @@ After=network.target
|
|||
[Service]
|
||||
ExecStart=/usr/bin/docker -d -H fd://
|
||||
Restart=on-failure
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -14,13 +14,15 @@
|
|||
# VMs, bare metal, OpenStack clusters, public clouds and more.
|
||||
### END INIT INFO
|
||||
|
||||
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
||||
|
||||
BASE=$(basename $0)
|
||||
|
||||
# modify these in /etc/default/$BASE (/etc/default/docker)
|
||||
DOCKER=/usr/bin/$BASE
|
||||
DOCKER_PIDFILE=/var/run/$BASE.pid
|
||||
DOCKER_OPTS=
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
||||
DOCKER_DESC="Docker"
|
||||
|
||||
# Get lsb functions
|
||||
. /lib/lsb/init-functions
|
||||
|
@ -30,8 +32,8 @@ if [ -f /etc/default/$BASE ]; then
|
|||
fi
|
||||
|
||||
# see also init_is_upstart in /lib/lsb/init-functions (which isn't available in Ubuntu 12.04, or we'd use it)
|
||||
if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | /bin/grep -q upstart; then
|
||||
log_failure_msg "Docker is managed via upstart, try using service $BASE $1"
|
||||
if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | grep -q upstart; then
|
||||
log_failure_msg "$DOCKER_DESC is managed via upstart, try using service $BASE $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -43,7 +45,7 @@ fi
|
|||
|
||||
fail_unless_root() {
|
||||
if [ "$(id -u)" != '0' ]; then
|
||||
log_failure_msg "Docker must be run as root"
|
||||
log_failure_msg "$DOCKER_DESC must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
@ -51,21 +53,37 @@ fail_unless_root() {
|
|||
case "$1" in
|
||||
start)
|
||||
fail_unless_root
|
||||
log_begin_msg "Starting Docker: $BASE"
|
||||
mount | grep cgroup >/dev/null || mount -t cgroup none /sys/fs/cgroup 2>/dev/null
|
||||
|
||||
if ! grep -q cgroup /proc/mounts; then
|
||||
# rough approximation of cgroupfs-mount
|
||||
mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
|
||||
for sys in $(cut -d' ' -f1 /proc/cgroups); do
|
||||
mkdir -p /sys/fs/cgroup/$sys
|
||||
if ! mount -n -t cgroup -o $sys cgroup /sys/fs/cgroup/$sys 2>/dev/null; then
|
||||
rmdir /sys/fs/cgroup/$sys 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
touch /var/log/docker.log
|
||||
chgrp docker /var/log/docker.log
|
||||
|
||||
log_begin_msg "Starting $DOCKER_DESC: $BASE"
|
||||
start-stop-daemon --start --background \
|
||||
--no-close \
|
||||
--exec "$DOCKER" \
|
||||
--pidfile "$DOCKER_PIDFILE" \
|
||||
-- -d -p "$DOCKER_PIDFILE" \
|
||||
$DOCKER_OPTS
|
||||
-- \
|
||||
-d -p "$DOCKER_PIDFILE" \
|
||||
$DOCKER_OPTS \
|
||||
> /var/log/docker.log 2>&1
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
stop)
|
||||
fail_unless_root
|
||||
log_begin_msg "Stopping Docker: $BASE"
|
||||
start-stop-daemon --stop \
|
||||
--pidfile "$DOCKER_PIDFILE"
|
||||
log_begin_msg "Stopping $DOCKER_DESC: $BASE"
|
||||
start-stop-daemon --stop --pidfile "$DOCKER_PIDFILE"
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
13
contrib/init/sysvinit-debian/docker.default
Normal file
13
contrib/init/sysvinit-debian/docker.default
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Docker Upstart and SysVinit configuration file
|
||||
|
||||
# Customize location of Docker binary (especially for development testing).
|
||||
#DOCKER="/usr/local/bin/docker"
|
||||
|
||||
# Use DOCKER_OPTS to modify the daemon startup options.
|
||||
#DOCKER_OPTS="-dns 8.8.8.8 -dns 8.8.4.4"
|
||||
|
||||
# If you need Docker to use an HTTP proxy, it can also be specified here.
|
||||
#export http_proxy="http://127.0.0.1:3128/"
|
||||
|
||||
# This is also a handy place to tweak where Docker's temporary files go.
|
||||
#export TMPDIR="/mnt/bigdrive/docker-tmp"
|
123
contrib/init/sysvinit-redhat/docker
Executable file
123
contrib/init/sysvinit-redhat/docker
Executable file
|
@ -0,0 +1,123 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# /etc/rc.d/init.d/docker
|
||||
#
|
||||
# Daemon for docker.io
|
||||
#
|
||||
# chkconfig: 2345 95 95
|
||||
# description: Daemon for docker.io
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: docker
|
||||
# Required-Start: $network cgconfig
|
||||
# Required-Stop:
|
||||
# Should-Start:
|
||||
# Should-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: start and stop docker
|
||||
# Description: Daemon for docker.io
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
. /etc/rc.d/init.d/functions
|
||||
|
||||
prog="docker"
|
||||
exec="/usr/bin/$prog"
|
||||
pidfile="/var/run/$prog.pid"
|
||||
lockfile="/var/lock/subsys/$prog"
|
||||
logfile="/var/log/$prog"
|
||||
|
||||
[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
|
||||
|
||||
prestart() {
|
||||
service cgconfig status > /dev/null
|
||||
|
||||
if [[ $? != 0 ]]; then
|
||||
service cgconfig start
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
start() {
|
||||
[ -x $exec ] || exit 5
|
||||
|
||||
if ! [ -f $pidfile ]; then
|
||||
prestart
|
||||
printf "Starting $prog:\t"
|
||||
echo "\n$(date)\n" >> $logfile
|
||||
$exec -d $other_args &>> $logfile &
|
||||
pid=$!
|
||||
touch $lockfile
|
||||
success
|
||||
echo
|
||||
else
|
||||
failure
|
||||
echo
|
||||
printf "$pidfile still exists...\n"
|
||||
exit 7
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo -n $"Stopping $prog: "
|
||||
killproc -p $pidfile $prog
|
||||
retval=$?
|
||||
echo
|
||||
[ $retval -eq 0 ] && rm -f $lockfile
|
||||
return $retval
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
reload() {
|
||||
restart
|
||||
}
|
||||
|
||||
force_reload() {
|
||||
restart
|
||||
}
|
||||
|
||||
rh_status() {
|
||||
status -p $pidfile $prog
|
||||
}
|
||||
|
||||
rh_status_q() {
|
||||
rh_status >/dev/null 2>&1
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
rh_status_q && exit 0
|
||||
$1
|
||||
;;
|
||||
stop)
|
||||
rh_status_q || exit 0
|
||||
$1
|
||||
;;
|
||||
restart)
|
||||
$1
|
||||
;;
|
||||
reload)
|
||||
rh_status_q || exit 7
|
||||
$1
|
||||
;;
|
||||
force-reload)
|
||||
force_reload
|
||||
;;
|
||||
status)
|
||||
rh_status
|
||||
;;
|
||||
condrestart|try-restart)
|
||||
rh_status_q || exit 0
|
||||
restart
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
|
||||
exit 2
|
||||
esac
|
||||
|
||||
exit $?
|
7
contrib/init/sysvinit-redhat/docker.sysconfig
Normal file
7
contrib/init/sysvinit-redhat/docker.sysconfig
Normal file
|
@ -0,0 +1,7 @@
|
|||
# /etc/sysconfig/docker
|
||||
#
|
||||
# Other arguments to pass to the docker daemon process
|
||||
# These will be parsed by the sysv initscript and appended
|
||||
# to the arguments list passed to docker -d
|
||||
|
||||
other_args=""
|
|
@ -1,15 +1,26 @@
|
|||
description "Docker daemon"
|
||||
|
||||
start on filesystem and started lxc-net
|
||||
start on filesystem
|
||||
stop on runlevel [!2345]
|
||||
|
||||
respawn
|
||||
|
||||
script
|
||||
# modify these in /etc/default/$UPSTART_JOB (/etc/default/docker)
|
||||
DOCKER=/usr/bin/$UPSTART_JOB
|
||||
DOCKER_OPTS=
|
||||
if [ -f /etc/default/$UPSTART_JOB ]; then
|
||||
. /etc/default/$UPSTART_JOB
|
||||
fi
|
||||
if ! grep -q cgroup /proc/mounts; then
|
||||
# rough approximation of cgroupfs-mount
|
||||
mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
|
||||
for sys in $(cut -d' ' -f1 /proc/cgroups); do
|
||||
mkdir -p /sys/fs/cgroup/$sys
|
||||
if ! mount -n -t cgroup -o $sys cgroup /sys/fs/cgroup/$sys 2>/dev/null; then
|
||||
rmdir /sys/fs/cgroup/$sys 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
"$DOCKER" -d $DOCKER_OPTS
|
||||
end script
|
||||
|
|
|
@ -39,7 +39,7 @@ arch-chroot $ROOTFS /bin/sh -c "haveged -w 1024; pacman-key --init; pkill havege
|
|||
arch-chroot $ROOTFS /bin/sh -c "ln -s /usr/share/zoneinfo/UTC /etc/localtime"
|
||||
echo 'en_US.UTF-8 UTF-8' > $ROOTFS/etc/locale.gen
|
||||
arch-chroot $ROOTFS locale-gen
|
||||
arch-chroot $ROOTFS /bin/sh -c 'echo "Server = http://mirrors.kernel.org/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist'
|
||||
arch-chroot $ROOTFS /bin/sh -c 'echo "Server = https://mirrors.kernel.org/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist'
|
||||
|
||||
# udev doesn't work in containers, rebuild /dev
|
||||
DEV=$ROOTFS/dev
|
||||
|
|
|
@ -44,6 +44,8 @@ debianStable=wheezy
|
|||
debianUnstable=sid
|
||||
# this should match the name found at http://releases.ubuntu.com/
|
||||
ubuntuLatestLTS=precise
|
||||
# this should match the name found at http://releases.tanglu.org/
|
||||
tangluLatest=aequorea
|
||||
|
||||
while getopts v:i:a:p:dst name; do
|
||||
case "$name" in
|
||||
|
@ -201,11 +203,23 @@ if [ -z "$strictDebootstrap" ]; then
|
|||
s/ $suite-updates main/ ${suite}-security main/
|
||||
" etc/apt/sources.list
|
||||
;;
|
||||
Tanglu)
|
||||
# add the updates repository
|
||||
if [ "$suite" = "$tangluLatest" ]; then
|
||||
# ${suite}-updates only applies to stable Tanglu versions
|
||||
sudo sed -i "p; s/ $suite main$/ ${suite}-updates main/" etc/apt/sources.list
|
||||
fi
|
||||
;;
|
||||
SteamOS)
|
||||
# add contrib and non-free
|
||||
sudo sed -i "s/ $suite main$/ $suite main contrib non-free/" etc/apt/sources.list
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# make sure our packages lists are as up to date as we can get them
|
||||
sudo chroot . apt-get update
|
||||
sudo chroot . apt-get dist-upgrade -y
|
||||
fi
|
||||
|
||||
if [ "$justTar" ]; then
|
||||
|
@ -248,6 +262,28 @@ else
|
|||
fi
|
||||
fi
|
||||
;;
|
||||
Tanglu)
|
||||
if [ "$suite" = "$tangluLatest" ]; then
|
||||
# tag latest
|
||||
$docker tag $repo:$suite $repo:latest
|
||||
fi
|
||||
if [ -r etc/lsb-release ]; then
|
||||
lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
|
||||
if [ "$lsbRelease" ]; then
|
||||
# tag specific Tanglu version number, if available (1.0, 2.0, etc.)
|
||||
$docker tag $repo:$suite $repo:$lsbRelease
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
SteamOS)
|
||||
if [ -r etc/lsb-release ]; then
|
||||
lsbRelease="$(. etc/lsb-release && echo "$DISTRIB_RELEASE")"
|
||||
if [ "$lsbRelease" ]; then
|
||||
# tag specific SteamOS version number, if available (1.0, 2.0, etc.)
|
||||
$docker tag $repo:$suite $repo:$lsbRelease
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -45,13 +45,21 @@ target=$(mktemp -d --tmpdir $(basename $0).XXXXXX)
|
|||
|
||||
set -x
|
||||
|
||||
for dev in console null zero urandom; do
|
||||
/sbin/MAKEDEV -d "$target"/dev -x $dev
|
||||
done
|
||||
mkdir -m 755 "$target"/dev
|
||||
mknod -m 600 "$target"/dev/console c 5 1
|
||||
mknod -m 600 "$target"/dev/initctl p
|
||||
mknod -m 666 "$target"/dev/full c 1 7
|
||||
mknod -m 666 "$target"/dev/null c 1 3
|
||||
mknod -m 666 "$target"/dev/ptmx c 5 2
|
||||
mknod -m 666 "$target"/dev/random c 1 8
|
||||
mknod -m 666 "$target"/dev/tty c 5 0
|
||||
mknod -m 666 "$target"/dev/tty0 c 4 0
|
||||
mknod -m 666 "$target"/dev/urandom c 1 9
|
||||
mknod -m 666 "$target"/dev/zero c 1 5
|
||||
|
||||
yum -c "$yum_config" --installroot="$target" --setopt=tsflags=nodocs \
|
||||
--setopt=group_package_types=mandatory -y groupinstall Core
|
||||
yum -c "$yum_config" --installroot="$mount" -y clean all
|
||||
yum -c "$yum_config" --installroot="$target" -y clean all
|
||||
|
||||
cat > "$target"/etc/sysconfig/network <<EOF
|
||||
NETWORKING=yes
|
||||
|
@ -76,7 +84,7 @@ rm -rf "$target"/var/cache/ldconfig/*
|
|||
|
||||
version=
|
||||
if [ -r "$target"/etc/redhat-release ]; then
|
||||
version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' /etc/redhat-release)"
|
||||
version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' "$target"/etc/redhat-release)"
|
||||
fi
|
||||
|
||||
if [ -z "$version" ]; then
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
#
|
||||
GH_USER=$(git config --get github.user)
|
||||
SOB=$(git var GIT_AUTHOR_IDENT | sed -n "s/^\(.*>\).*$/Docker-DCO-1.1-Signed-off-by: \1 \(github: $GH_USER\)/p")
|
||||
grep -qs "^$SOB" "$1" || echo "\n$SOB" >> "$1"
|
||||
grep -qs "^$SOB" "$1" || {
|
||||
echo
|
||||
echo "$SOB"
|
||||
} >> "$1"
|
||||
|
|
16
contrib/syntax/textmate/Docker.tmbundle/info.plist
Normal file
16
contrib/syntax/textmate/Docker.tmbundle/info.plist
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>contactEmailRot13</key>
|
||||
<string>germ@andz.com.ar</string>
|
||||
<key>contactName</key>
|
||||
<string>GermanDZ</string>
|
||||
<key>description</key>
|
||||
<string>Helpers for Docker.</string>
|
||||
<key>name</key>
|
||||
<string>Docker</string>
|
||||
<key>uuid</key>
|
||||
<string>8B9DDBAF-E65C-4E12-FFA7-467D4AA535B1</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,4 +1,4 @@
|
|||
# Dockerfile.tmLanguage
|
||||
# Docker.tmbundle
|
||||
|
||||
Dockerfile syntaxt highlighting for TextMate and Sublime Text.
|
||||
|
||||
|
@ -6,11 +6,11 @@ Dockerfile syntaxt highlighting for TextMate and Sublime Text.
|
|||
|
||||
### Sublime Text
|
||||
|
||||
Available for Sublime Text under [package control](https://sublime.wbond.net/packages/Dockerfile%20Syntax%20Highlighting).
|
||||
Available for Sublime Text under [package control](https://sublime.wbond.net/packages/Dockerfile%20Syntax%20Highlighting).
|
||||
Search for *Dockerfile Syntax Highlighting*
|
||||
|
||||
### TextMate
|
||||
### TextMate 2
|
||||
|
||||
*...unknown. Probably put it somewhere smart.*
|
||||
Copy the directory `Docker.tmbundle` (showed as a Package in OSX) to `~/Library/Application Support/TextMate/Managed/Bundles`
|
||||
|
||||
enjoy.
|
||||
|
|
|
@ -31,7 +31,7 @@ stop on runlevel [!2345]
|
|||
respawn
|
||||
|
||||
script
|
||||
/usr/bin/docker -d -H=tcp://0.0.0.0:4243/
|
||||
/usr/bin/docker -d -H=tcp://0.0.0.0:4243
|
||||
end script
|
||||
```
|
||||
|
||||
|
|
103
docker/docker.go
103
docker/docker.go
|
@ -6,21 +6,18 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dotcloud/docker"
|
||||
"github.com/dotcloud/docker/api"
|
||||
"github.com/dotcloud/docker/builtins"
|
||||
"github.com/dotcloud/docker/dockerversion"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
flag "github.com/dotcloud/docker/pkg/mflag"
|
||||
"github.com/dotcloud/docker/pkg/opts"
|
||||
"github.com/dotcloud/docker/sysinit"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
GITCOMMIT string
|
||||
VERSION string
|
||||
)
|
||||
|
||||
func main() {
|
||||
if selfPath := utils.SelfPath(); selfPath == "/sbin/init" || selfPath == "/.dockerinit" {
|
||||
if selfPath := utils.SelfPath(); strings.Contains(selfPath, ".dockerinit") {
|
||||
// Running in init mode
|
||||
sysinit.SysInit()
|
||||
return
|
||||
|
@ -35,15 +32,17 @@ func main() {
|
|||
bridgeIp = flag.String([]string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
|
||||
pidfile = flag.String([]string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file")
|
||||
flRoot = flag.String([]string{"g", "-graph"}, "/var/lib/docker", "Path to use as the root of the docker runtime")
|
||||
flSocketGroup = flag.String([]string{"G", "-group"}, "docker", "Group to assign the unix socket specified by -H when running in daemon mode; use '' (the empty string) to disable setting of a group")
|
||||
flEnableCors = flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"}, false, "Enable CORS headers in the remote API")
|
||||
flDns = docker.NewListOpts(docker.ValidateIp4Address)
|
||||
flDns = opts.NewListOpts(opts.ValidateIp4Address)
|
||||
flEnableIptables = flag.Bool([]string{"#iptables", "-iptables"}, true, "Disable docker's addition of iptables rules")
|
||||
flEnableIpForward = flag.Bool([]string{"#ip-forward", "-ip-forward"}, true, "Disable enabling of net.ipv4.ip_forward")
|
||||
flDefaultIp = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")
|
||||
flInterContainerComm = flag.Bool([]string{"#icc", "-icc"}, true, "Enable inter-container communication")
|
||||
flGraphDriver = flag.String([]string{"s", "-storage-driver"}, "", "Force the docker runtime to use a specific storage driver")
|
||||
flHosts = docker.NewListOpts(docker.ValidateHost)
|
||||
flMtu = flag.Int([]string{"#mtu", "-mtu"}, 0, "Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if not default route is available")
|
||||
flExecDriver = flag.String([]string{"e", "-exec-driver"}, "native", "Force the docker runtime to use a specific exec driver")
|
||||
flHosts = opts.NewListOpts(api.ValidateHost)
|
||||
flMtu = flag.Int([]string{"#mtu", "-mtu"}, 0, "Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available")
|
||||
)
|
||||
flag.Var(&flDns, []string{"#dns", "-dns"}, "Force docker to use specific DNS servers")
|
||||
flag.Var(&flHosts, []string{"H", "-host"}, "tcp://host:port, unix://path/to/socket, fd://* or fd://socketfd to use in daemon mode. Multiple sockets can be specified")
|
||||
|
@ -61,6 +60,9 @@ func main() {
|
|||
// If we do not have a host, default to unix socket
|
||||
defaultHost = fmt.Sprintf("unix://%s", api.DEFAULTUNIXSOCKET)
|
||||
}
|
||||
if _, err := api.ValidateHost(defaultHost); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
flHosts.Set(defaultHost)
|
||||
}
|
||||
|
||||
|
@ -71,40 +73,73 @@ func main() {
|
|||
if *flDebug {
|
||||
os.Setenv("DEBUG", "1")
|
||||
}
|
||||
docker.GITCOMMIT = GITCOMMIT
|
||||
docker.VERSION = VERSION
|
||||
if *flDaemon {
|
||||
if flag.NArg() != 0 {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
eng, err := engine.New(*flRoot)
|
||||
// set up the TempDir to use a canonical path
|
||||
tmp := os.TempDir()
|
||||
realTmp, err := utils.ReadSymlinkedDirectory(tmp)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to get the full path to the TempDir (%s): %s", tmp, err)
|
||||
}
|
||||
os.Setenv("TMPDIR", realTmp)
|
||||
|
||||
// get the canonical path to the Docker root directory
|
||||
root := *flRoot
|
||||
var realRoot string
|
||||
if _, err := os.Stat(root); err != nil && os.IsNotExist(err) {
|
||||
realRoot = root
|
||||
} else {
|
||||
realRoot, err = utils.ReadSymlinkedDirectory(root)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to get the full path to root (%s): %s", root, err)
|
||||
}
|
||||
}
|
||||
|
||||
eng, err := engine.New(realRoot)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Load plugin: httpapi
|
||||
job := eng.Job("initserver")
|
||||
job.Setenv("Pidfile", *pidfile)
|
||||
job.Setenv("Root", *flRoot)
|
||||
job.SetenvBool("AutoRestart", *flAutoRestart)
|
||||
job.SetenvList("Dns", flDns.GetAll())
|
||||
job.SetenvBool("EnableIptables", *flEnableIptables)
|
||||
job.SetenvBool("EnableIpForward", *flEnableIpForward)
|
||||
job.Setenv("BridgeIface", *bridgeName)
|
||||
job.Setenv("BridgeIP", *bridgeIp)
|
||||
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)
|
||||
}
|
||||
// Load builtins
|
||||
builtins.Register(eng)
|
||||
// load the daemon in the background so we can immediately start
|
||||
// the http api so that connections don't fail while the daemon
|
||||
// is booting
|
||||
go func() {
|
||||
// Load plugin: httpapi
|
||||
job := eng.Job("initserver")
|
||||
job.Setenv("Pidfile", *pidfile)
|
||||
job.Setenv("Root", realRoot)
|
||||
job.SetenvBool("AutoRestart", *flAutoRestart)
|
||||
job.SetenvList("Dns", flDns.GetAll())
|
||||
job.SetenvBool("EnableIptables", *flEnableIptables)
|
||||
job.SetenvBool("EnableIpForward", *flEnableIpForward)
|
||||
job.Setenv("BridgeIface", *bridgeName)
|
||||
job.Setenv("BridgeIP", *bridgeIp)
|
||||
job.Setenv("DefaultIp", *flDefaultIp)
|
||||
job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
|
||||
job.Setenv("GraphDriver", *flGraphDriver)
|
||||
job.Setenv("ExecDriver", *flExecDriver)
|
||||
job.SetenvInt("Mtu", *flMtu)
|
||||
if err := job.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// after the daemon is done setting up we can tell the api to start
|
||||
// accepting connections
|
||||
if err := eng.Job("acceptconnections").Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Serve api
|
||||
job = eng.Job("serveapi", flHosts.GetAll()...)
|
||||
job := eng.Job("serveapi", flHosts.GetAll()...)
|
||||
job.SetenvBool("Logging", true)
|
||||
job.SetenvBool("EnableCors", *flEnableCors)
|
||||
job.Setenv("Version", VERSION)
|
||||
job.Setenv("Version", dockerversion.VERSION)
|
||||
job.Setenv("SocketGroup", *flSocketGroup)
|
||||
if err := job.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -113,7 +148,7 @@ func main() {
|
|||
log.Fatal("Please specify only one -H")
|
||||
}
|
||||
protoAddrParts := strings.SplitN(flHosts.GetAll()[0], "://", 2)
|
||||
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
|
||||
if err := api.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
|
||||
if sterr, ok := err.(*utils.StatusError); ok {
|
||||
if sterr.Status != "" {
|
||||
log.Println(sterr.Status)
|
||||
|
@ -126,5 +161,5 @@ func main() {
|
|||
}
|
||||
|
||||
func showVersion() {
|
||||
fmt.Printf("Docker version %s, build %s\n", VERSION, GITCOMMIT)
|
||||
fmt.Printf("Docker version %s, build %s\n", dockerversion.VERSION, dockerversion.GITCOMMIT)
|
||||
}
|
||||
|
|
|
@ -4,11 +4,6 @@ import (
|
|||
"github.com/dotcloud/docker/sysinit"
|
||||
)
|
||||
|
||||
var (
|
||||
GITCOMMIT string
|
||||
VERSION string
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Running in init mode
|
||||
sysinit.SysInit()
|
||||
|
|
15
dockerversion/dockerversion.go
Normal file
15
dockerversion/dockerversion.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package dockerversion
|
||||
|
||||
// FIXME: this should be embedded in the docker/docker.go,
|
||||
// but we can't because distro policy requires us to
|
||||
// package a separate dockerinit binary, and that binary needs
|
||||
// to know its version too.
|
||||
|
||||
var (
|
||||
GITCOMMIT string
|
||||
VERSION string
|
||||
|
||||
IAMSTATIC bool // whether or not Docker itself was compiled statically via ./hack/make.sh binary
|
||||
INITSHA1 string // sha1sum of separate static dockerinit, if Docker itself was compiled dynamically via ./hack/make.sh dynbinary
|
||||
INITPATH string // custom location to search for a valid dockerinit binary (available for packagers as a last resort escape hatch)
|
||||
)
|
|
@ -19,10 +19,24 @@ post-commit hooks. The "release" branch maps to the "latest"
|
|||
documentation and the "master" branch maps to the "master"
|
||||
documentation.
|
||||
|
||||
**Warning**: The "master" documentation may include features not yet
|
||||
part of any official docker release. "Master" docs should be used only
|
||||
for understanding bleeding-edge development and "latest" should be
|
||||
used for the latest official release.
|
||||
## Branches
|
||||
|
||||
**There are two branches related to editing docs**: ``master`` and a
|
||||
``doc*`` branch (currently ``doc0.8.1``). You should normally edit
|
||||
docs on the ``master`` branch. That way your fixes will automatically
|
||||
get included in later releases, and docs maintainers can easily
|
||||
cherry-pick your changes to bring over to the current docs branch. In
|
||||
the rare case where your change is not forward-compatible, then you
|
||||
could base your change on the appropriate ``doc*`` branch.
|
||||
|
||||
Now that we have a ``doc*`` branch, we can keep the ``latest`` docs
|
||||
up to date with any bugs found between ``docker`` code releases.
|
||||
|
||||
**Warning**: When *reading* the docs, the ``master`` documentation may
|
||||
include features not yet part of any official docker
|
||||
release. ``Master`` docs should be used only for understanding
|
||||
bleeding-edge development and ``latest`` (which points to the ``doc*``
|
||||
branch``) should be used for the latest official release.
|
||||
|
||||
If you need to manually trigger a build of an existing branch, then
|
||||
you can do that through the [readthedocs
|
||||
|
@ -39,7 +53,7 @@ Getting Started
|
|||
To edit and test the docs, you'll need to install the Sphinx tool and
|
||||
its dependencies. There are two main ways to install this tool:
|
||||
|
||||
###Native Installation
|
||||
### Native Installation
|
||||
|
||||
Install dependencies from `requirements.txt` file in your `docker/docs`
|
||||
directory:
|
||||
|
@ -48,7 +62,7 @@ directory:
|
|||
|
||||
* Mac OS X: `[sudo] pip-2.7 install -r docs/requirements.txt`
|
||||
|
||||
###Alternative Installation: Docker Container
|
||||
### Alternative Installation: Docker Container
|
||||
|
||||
If you're running ``docker`` on your development machine then you may
|
||||
find it easier and cleaner to use the docs Dockerfile. This installs Sphinx
|
||||
|
@ -59,11 +73,16 @@ docs inside the container, even starting a simple HTTP server on port
|
|||
In the ``docker`` source directory, run:
|
||||
```make docs```
|
||||
|
||||
This is the equivalent to ``make clean server`` since each container starts clean.
|
||||
This is the equivalent to ``make clean server`` since each container
|
||||
starts clean.
|
||||
|
||||
Usage
|
||||
-----
|
||||
* Follow the contribution guidelines (``../CONTRIBUTING.md``)
|
||||
# Contributing
|
||||
|
||||
## Normal Case:
|
||||
|
||||
* Follow the contribution guidelines ([see
|
||||
``../CONTRIBUTING.md``](../CONTRIBUTING.md)).
|
||||
* [Remember to sign your work!](../CONTRIBUTING.md#sign-your-work)
|
||||
* Work in your own fork of the code, we accept pull requests.
|
||||
* Change the ``.rst`` files with your favorite editor -- try to keep the
|
||||
lines short and respect RST and Sphinx conventions.
|
||||
|
@ -75,6 +94,20 @@ Usage
|
|||
|
||||
``make clean docs`` must complete without any warnings or errors.
|
||||
|
||||
## Special Case for RST Newbies:
|
||||
|
||||
If you want to write a new doc or make substantial changes to an
|
||||
existing doc, but **you don't know RST syntax**, we will accept pull
|
||||
requests in Markdown and plain text formats. We really want to
|
||||
encourage people to share their knowledge and don't want the markup
|
||||
syntax to be the obstacle. So when you make the Pull Request, please
|
||||
note in your comment that you need RST markup assistance, and we'll
|
||||
make the changes for you, and then we will make a pull request to your
|
||||
pull request so that you can get all the changes and learn about the
|
||||
markup. You still need to follow the
|
||||
[``CONTRIBUTING``](../CONTRIBUTING) guidelines, so please sign your
|
||||
commits.
|
||||
|
||||
Working using GitHub's file editor
|
||||
----------------------------------
|
||||
|
||||
|
@ -82,6 +115,7 @@ Alternatively, for small changes and typos you might want to use
|
|||
GitHub's built in file editor. It allows you to preview your changes
|
||||
right online (though there can be some differences between GitHub
|
||||
markdown and Sphinx RST). Just be careful not to create many commits.
|
||||
And you must still [sign your work!](../CONTRIBUTING.md#sign-your-work)
|
||||
|
||||
Images
|
||||
------
|
||||
|
@ -93,8 +127,11 @@ exists.
|
|||
|
||||
Notes
|
||||
-----
|
||||
* For the template the css is compiled from less. When changes are needed they can be compiled using
|
||||
lessc ``lessc main.less`` or watched using watch-lessc ``watch-lessc -i main.less -o main.css``
|
||||
|
||||
* For the template the css is compiled from less. When changes are
|
||||
needed they can be compiled using
|
||||
|
||||
lessc ``lessc main.less`` or watched using watch-lessc ``watch-lessc -i main.less -o main.css``
|
||||
|
||||
Guides on using sphinx
|
||||
----------------------
|
||||
|
@ -106,7 +143,8 @@ Guides on using sphinx
|
|||
Hello world
|
||||
===========
|
||||
|
||||
This is.. (etc.)
|
||||
This is a reference to :ref:`hello_world` and will work even if we
|
||||
move the target to another file or change the title of the section.
|
||||
```
|
||||
|
||||
The ``_hello_world:`` will make it possible to link to this position
|
||||
|
|
|
@ -13,8 +13,8 @@ The specific process will depend heavily on the Linux distribution you
|
|||
want to package. We have some examples below, and you are encouraged
|
||||
to submit pull requests to contribute new ones.
|
||||
|
||||
Getting Started
|
||||
...............
|
||||
Create a full image using tar
|
||||
.............................
|
||||
|
||||
In general, you'll want to start with a working machine that is
|
||||
running the distribution you'd like to package as a base image, though
|
||||
|
@ -44,3 +44,22 @@ Docker GitHub Repo:
|
|||
<https://github.com/dotcloud/docker/blob/master/contrib/mkimage-yum.sh>`_
|
||||
* `Debian / Ubuntu
|
||||
<https://github.com/dotcloud/docker/blob/master/contrib/mkimage-debootstrap.sh>`_
|
||||
|
||||
|
||||
Creating a simple base image using ``scratch``
|
||||
..............................................
|
||||
|
||||
There is a special repository in the Docker registry called ``scratch``, which
|
||||
was created using an empty tar file::
|
||||
|
||||
$ tar cv --files-from /dev/null | docker import - scratch
|
||||
|
||||
which you can ``docker pull``. You can then use that image to base your new
|
||||
minimal containers ``FROM``::
|
||||
|
||||
FROM scratch
|
||||
ADD true-asm /true
|
||||
CMD ["/true"]
|
||||
|
||||
The Dockerfile above is from extremely minimal image -
|
||||
`tianon/true <https://github.com/tianon/dockerfiles/tree/master/true>`_.
|
||||
|
|
|
@ -24,7 +24,17 @@ a working, up-to-date docker installation, then continue to the next
|
|||
step.
|
||||
|
||||
|
||||
Step 2: Check out the Source
|
||||
Step 2: Install tools used for this tutorial
|
||||
--------------------------------------------
|
||||
|
||||
Install ``git``; honest, it's very good. You can use other ways to get the Docker
|
||||
source, but they're not anywhere near as easy.
|
||||
|
||||
Install ``make``. This tutorial uses our base Makefile to kick off the docker
|
||||
containers in a repeatable and consistent way. Again, you can do it in other ways
|
||||
but you need to do more work.
|
||||
|
||||
Step 3: Check out the Source
|
||||
----------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -35,7 +45,7 @@ Step 2: Check out the Source
|
|||
To checkout a different revision just use ``git checkout`` with the name of branch or revision number.
|
||||
|
||||
|
||||
Step 3: Build the Environment
|
||||
Step 4: Build the Environment
|
||||
-----------------------------
|
||||
|
||||
This following command will build a development environment using the Dockerfile in the current directory. Essentially, it will install all the build and runtime dependencies necessary to build and test Docker. This command will take some time to complete when you first execute it.
|
||||
|
@ -48,7 +58,7 @@ If the build is successful, congratulations! You have produced a clean build of
|
|||
docker, neatly encapsulated in a standard build environment.
|
||||
|
||||
|
||||
Step 4: Build the Docker Binary
|
||||
Step 5: Build the Docker Binary
|
||||
-------------------------------
|
||||
|
||||
To create the Docker binary, run this command:
|
||||
|
@ -82,14 +92,6 @@ To execute the test cases, run this command:
|
|||
|
||||
sudo make test
|
||||
|
||||
|
||||
Note: if you're running the tests in vagrant, you need to specify a dns entry in
|
||||
the command (either edit the Makefile, or run the step manually):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run -dns 8.8.8.8 -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test
|
||||
|
||||
If the test are successful then the tail of the output should look something like this
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -120,7 +122,10 @@ If the test are successful then the tail of the output should look something lik
|
|||
PASS
|
||||
ok github.com/dotcloud/docker/utils 0.017s
|
||||
|
||||
If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'.
|
||||
You can use this to select certain tests to run, eg.
|
||||
|
||||
TESTFLAGS='-run ^TestBuild$' make test
|
||||
|
||||
|
||||
Step 6: Use Docker
|
||||
|
|
|
@ -55,7 +55,7 @@ The first two steps can be done as part of a Dockerfile, as follows.
|
|||
FROM ubuntu
|
||||
MAINTAINER Eystein Måløy Stenberg <eytein.stenberg@gmail.com>
|
||||
|
||||
RUN apt-get -y install wget lsb-release unzip
|
||||
RUN apt-get -y install wget lsb-release unzip ca-certificates
|
||||
|
||||
# install latest CFEngine
|
||||
RUN wget -qO- http://cfengine.com/pub/gpg.key | apt-key add -
|
||||
|
@ -64,7 +64,7 @@ The first two steps can be done as part of a Dockerfile, as follows.
|
|||
RUN apt-get install cfengine-community
|
||||
|
||||
# install cfe-docker process management policy
|
||||
RUN wget --no-check-certificate https://github.com/estenberg/cfe-docker/archive/master.zip -P /tmp/ && unzip /tmp/master.zip -d /tmp/
|
||||
RUN wget https://github.com/estenberg/cfe-docker/archive/master.zip -P /tmp/ && unzip /tmp/master.zip -d /tmp/
|
||||
RUN cp /tmp/cfe-docker-master/cfengine/bin/* /var/cfengine/bin/
|
||||
RUN cp /tmp/cfe-docker-master/cfengine/inputs/* /var/cfengine/inputs/
|
||||
RUN rm -rf /tmp/cfe-docker-master /tmp/master.zip
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
:description: A simple hello world example with Docker
|
||||
:keywords: docker, example, hello world
|
||||
|
||||
.. _examples:
|
||||
|
||||
Hello World
|
||||
-----------
|
||||
|
||||
.. _running_examples:
|
||||
|
||||
Check your Docker install
|
||||
|
@ -18,7 +13,7 @@ your Docker install, run the following command:
|
|||
.. code-block:: bash
|
||||
|
||||
# Check that you have a working install
|
||||
docker info
|
||||
$ sudo docker info
|
||||
|
||||
If you get ``docker: command not found`` or something like
|
||||
``/var/lib/docker/repositories: permission denied`` you may have an incomplete
|
||||
|
@ -30,27 +25,28 @@ Please refer to :ref:`installation_list` for installation instructions.
|
|||
.. _hello_world:
|
||||
|
||||
Hello World
|
||||
===========
|
||||
-----------
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
This is the most basic example available for using Docker.
|
||||
|
||||
Download the base image which is named ``ubuntu``:
|
||||
Download the small base image named ``busybox``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Download an ubuntu image
|
||||
sudo docker pull ubuntu
|
||||
# Download a busybox image
|
||||
$ sudo docker pull busybox
|
||||
|
||||
Alternatively to the ``ubuntu`` image, you can select ``busybox``, a bare
|
||||
minimal Linux system. The images are retrieved from the Docker
|
||||
repository.
|
||||
The ``busybox`` image is a minimal Linux system. You can do the same
|
||||
with any number of other images, such as ``debian``, ``ubuntu`` or ``centos``.
|
||||
The images can be found and retrieved using the `Docker index`_.
|
||||
|
||||
.. _Docker index: http://index.docker.io
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run ubuntu /bin/echo hello world
|
||||
$ sudo docker run busybox /bin/echo hello world
|
||||
|
||||
This command will run a simple ``echo`` command, that will echo ``hello world`` back to the console over standard out.
|
||||
|
||||
|
@ -58,7 +54,7 @@ This command will run a simple ``echo`` command, that will echo ``hello world``
|
|||
|
||||
- **"sudo"** execute the following commands as user *root*
|
||||
- **"docker run"** run a command in a new container
|
||||
- **"ubuntu"** is the image we want to run the command inside of.
|
||||
- **"busybox"** is the image we are running the command in.
|
||||
- **"/bin/echo"** is the command we want to run in the container
|
||||
- **"hello world"** is the input for the echo command
|
||||
|
||||
|
@ -73,8 +69,8 @@ See the example in action
|
|||
<iframe width="560" height="400" frameborder="0"
|
||||
sandbox="allow-same-origin allow-scripts"
|
||||
srcdoc="<body><script type="text/javascript"
|
||||
src="https://asciinema.org/a/2603.js"
|
||||
id="asciicast-2603" async></script></body>">
|
||||
src="https://asciinema.org/a/7658.js"
|
||||
id="asciicast-7658" async></script></body>">
|
||||
</iframe>
|
||||
|
||||
----
|
||||
|
@ -82,7 +78,7 @@ See the example in action
|
|||
.. _hello_world_daemon:
|
||||
|
||||
Hello World Daemon
|
||||
==================
|
||||
------------------
|
||||
|
||||
.. include:: example_header.inc
|
||||
|
||||
|
@ -172,14 +168,14 @@ See the example in action
|
|||
id="asciicast-2562" async></script></body>">
|
||||
</iframe>
|
||||
|
||||
The next example in the series is a :ref:`python_web_app` example, or
|
||||
The next example in the series is a :ref:`nodejs_web_app` example, or
|
||||
you could skip to any of the other examples:
|
||||
|
||||
|
||||
* :ref:`python_web_app`
|
||||
* :ref:`nodejs_web_app`
|
||||
* :ref:`running_redis_service`
|
||||
* :ref:`running_ssh_service`
|
||||
* :ref:`running_couchdb_service`
|
||||
* :ref:`postgresql_service`
|
||||
* :ref:`mongodb_image`
|
||||
* :ref:`python_web_app`
|
||||
|
|
|
@ -16,7 +16,6 @@ to more substantial services like those which you might find in production.
|
|||
:maxdepth: 1
|
||||
|
||||
hello_world
|
||||
python_web_app
|
||||
nodejs_web_app
|
||||
running_redis_service
|
||||
running_ssh_service
|
||||
|
@ -26,3 +25,4 @@ to more substantial services like those which you might find in production.
|
|||
running_riak_service
|
||||
using_supervisord
|
||||
cfengine_process_management
|
||||
python_web_app
|
||||
|
|
53
docs/sources/examples/postgresql_service.Dockerfile
Normal file
53
docs/sources/examples/postgresql_service.Dockerfile
Normal file
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# example Dockerfile for http://docs.docker.io/en/latest/examples/postgresql_service/
|
||||
#
|
||||
|
||||
FROM ubuntu
|
||||
MAINTAINER SvenDowideit@docker.com
|
||||
|
||||
# Add the PostgreSQL PGP key to verify their Debian packages.
|
||||
# It should be the same key as https://www.postgresql.org/media/keys/ACCC4CF8.asc
|
||||
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8
|
||||
|
||||
# Add PostgreSQL's repository. It contains the most recent stable release
|
||||
# of PostgreSQL, ``9.3``.
|
||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list
|
||||
|
||||
# Update the Ubuntu and PostgreSQL repository indexes
|
||||
RUN apt-get update
|
||||
|
||||
# Install ``python-software-properties``, ``software-properties-common`` and PostgreSQL 9.3
|
||||
# There are some warnings (in red) that show up during the build. You can hide
|
||||
# them by prefixing each apt-get statement with DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get -y -q install python-software-properties software-properties-common
|
||||
RUN apt-get -y -q install postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3
|
||||
|
||||
# Note: The official Debian and Ubuntu images automatically ``apt-get clean``
|
||||
# after each ``apt-get``
|
||||
|
||||
# Run the rest of the commands as the ``postgres`` user created by the ``postgres-9.3`` package when it was ``apt-get installed``
|
||||
USER postgres
|
||||
|
||||
# Create a PostgreSQL role named ``docker`` with ``docker`` as the password and
|
||||
# then create a database `docker` owned by the ``docker`` role.
|
||||
# Note: here we use ``&&\`` to run commands one after the other - the ``\``
|
||||
# allows the RUN command to span multiple lines.
|
||||
RUN /etc/init.d/postgresql start &&\
|
||||
psql --command "CREATE USER docker WITH SUPERUSER PASSWORD 'docker';" &&\
|
||||
createdb -O docker docker
|
||||
|
||||
# Adjust PostgreSQL configuration so that remote connections to the
|
||||
# database are possible.
|
||||
RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/9.3/main/pg_hba.conf
|
||||
|
||||
# And add ``listen_addresses`` to ``/etc/postgresql/9.3/main/postgresql.conf``
|
||||
RUN echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
|
||||
|
||||
# Expose the PostgreSQL port
|
||||
EXPOSE 5432
|
||||
|
||||
# Add VOLUMEs to allow backup of config, logs and databases
|
||||
VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]
|
||||
|
||||
# Set the default command to run when starting the container
|
||||
CMD ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]
|
|
@ -9,152 +9,109 @@ PostgreSQL Service
|
|||
|
||||
.. include:: example_header.inc
|
||||
|
||||
.. note::
|
||||
|
||||
A shorter version of `this blog post`_.
|
||||
|
||||
.. _this blog post: http://zaiste.net/2013/08/docker_postgresql_how_to/
|
||||
|
||||
Installing PostgreSQL on Docker
|
||||
-------------------------------
|
||||
|
||||
Run an interactive shell in a Docker container.
|
||||
Assuming there is no Docker image that suits your needs in `the index`_, you
|
||||
can create one yourself.
|
||||
|
||||
.. code-block:: bash
|
||||
.. _the index: http://index.docker.io
|
||||
|
||||
sudo docker run -i -t ubuntu /bin/bash
|
||||
|
||||
Update its dependencies.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
apt-get update
|
||||
|
||||
Install ``python-software-properties``, ``software-properties-common``, ``wget`` and ``vim``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
apt-get -y install python-software-properties software-properties-common wget vim
|
||||
|
||||
Add PostgreSQL's repository. It contains the most recent stable release
|
||||
of PostgreSQL, ``9.3``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list
|
||||
apt-get update
|
||||
|
||||
Finally, install PostgreSQL 9.3
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
apt-get -y install postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3
|
||||
|
||||
Now, create a PostgreSQL superuser role that can create databases and
|
||||
other roles. Following Vagrant's convention the role will be named
|
||||
``docker`` with ``docker`` password assigned to it.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
su postgres -c "createuser -P -d -r -s docker"
|
||||
|
||||
Create a test database also named ``docker`` owned by previously created ``docker``
|
||||
role.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
su postgres -c "createdb -O docker docker"
|
||||
|
||||
Adjust PostgreSQL configuration so that remote connections to the
|
||||
database are possible. Make sure that inside
|
||||
``/etc/postgresql/9.3/main/pg_hba.conf`` you have following line:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
host all all 0.0.0.0/0 md5
|
||||
|
||||
Additionaly, inside ``/etc/postgresql/9.3/main/postgresql.conf``
|
||||
uncomment ``listen_addresses`` like so:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
listen_addresses='*'
|
||||
Start by creating a new Dockerfile:
|
||||
|
||||
.. note::
|
||||
|
||||
This PostgreSQL setup is for development only purposes. Refer
|
||||
to PostgreSQL documentation how to fine-tune these settings so that it
|
||||
is secure enough.
|
||||
to the PostgreSQL documentation to fine-tune these settings so that it
|
||||
is suitably secure.
|
||||
|
||||
Exit.
|
||||
.. literalinclude:: postgresql_service.Dockerfile
|
||||
|
||||
Build an image from the Dockerfile assign it a name.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
exit
|
||||
$ sudo docker build -t eg_postgresql .
|
||||
|
||||
Create an image from our container and assign it a name. The ``<container_id>``
|
||||
is in the Bash prompt; you can also locate it using ``docker ps -a``.
|
||||
And run the PostgreSQL server container (in the foreground):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker commit <container_id> <your username>/postgresql
|
||||
$ sudo docker run -rm -P -name pg_test eg_postgresql
|
||||
|
||||
Finally, run the PostgreSQL server via ``docker``.
|
||||
There are 2 ways to connect to the PostgreSQL server. We can use
|
||||
:ref:`working_with_links_names`, or we can access it from our host (or the network).
|
||||
|
||||
.. note:: The ``-rm`` removes the container and its image when the container
|
||||
exists successfully.
|
||||
|
||||
Using container linking
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Containers can be linked to another container's ports directly using
|
||||
``-link remote_name:local_alias`` in the client's ``docker run``. This will
|
||||
set a number of environment variables that can then be used to connect:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
CONTAINER=$(sudo docker run -d -p 5432 \
|
||||
-t <your username>/postgresql \
|
||||
/bin/su postgres -c '/usr/lib/postgresql/9.3/bin/postgres \
|
||||
-D /var/lib/postgresql/9.3/main \
|
||||
-c config_file=/etc/postgresql/9.3/main/postgresql.conf')
|
||||
$ sudo docker run -rm -t -i -link pg_test:pg eg_postgresql bash
|
||||
|
||||
Connect the PostgreSQL server using ``psql`` (You will need the
|
||||
postgresql client installed on the machine. For ubuntu, use something
|
||||
like ``sudo apt-get install postgresql-client``).
|
||||
postgres@7ef98b1b7243:/$ psql -h $PG_PORT_5432_TCP_ADDR -p $PG_PORT_5432_TCP_PORT -d docker -U docker --password
|
||||
|
||||
Connecting from your host system
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Assuming you have the postgresql-client installed, you can use the host-mapped port
|
||||
to test as well. You need to use ``docker ps`` to find out what local host port the
|
||||
container is mapped to first:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
CONTAINER_IP=$(sudo docker inspect -format='{{.NetworkSettings.IPAddress}}' $CONTAINER)
|
||||
psql -h $CONTAINER_IP -p 5432 -d docker -U docker -W
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
5e24362f27f6 eg_postgresql:latest /usr/lib/postgresql/ About an hour ago Up About an hour 0.0.0.0:49153->5432/tcp pg_test
|
||||
$ psql -h localhost -p 49153 -d docker -U docker --password
|
||||
|
||||
As before, create roles or databases if needed.
|
||||
Testing the database
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you have authenticated and have a ``docker =#`` prompt, you can
|
||||
create a table and populate it.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
psql (9.3.1)
|
||||
Type "help" for help.
|
||||
|
||||
docker=# CREATE DATABASE foo OWNER=docker;
|
||||
CREATE DATABASE
|
||||
docker=# CREATE TABLE cities (
|
||||
docker(# name varchar(80),
|
||||
docker(# location point
|
||||
docker(# );
|
||||
CREATE TABLE
|
||||
docker=# INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)');
|
||||
INSERT 0 1
|
||||
docker=# select * from cities;
|
||||
name | location
|
||||
---------------+-----------
|
||||
San Francisco | (-194,53)
|
||||
(1 row)
|
||||
|
||||
Additionally, publish your newly created image on the Docker Index.
|
||||
Using the container volumes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can use the defined volumes to inspect the PostgreSQL log files and to backup your
|
||||
configuration and data:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker login
|
||||
Username: <your username>
|
||||
[...]
|
||||
docker run -rm --volumes-from pg_test -t -i busybox sh
|
||||
|
||||
.. code-block:: bash
|
||||
/ # ls
|
||||
bin etc lib linuxrc mnt proc run sys usr
|
||||
dev home lib64 media opt root sbin tmp var
|
||||
/ # ls /etc/postgresql/9.3/main/
|
||||
environment pg_hba.conf postgresql.conf
|
||||
pg_ctl.conf pg_ident.conf start.conf
|
||||
/tmp # ls /var/log
|
||||
ldconfig postgresql
|
||||
|
||||
sudo docker push <your username>/postgresql
|
||||
|
||||
PostgreSQL service auto-launch
|
||||
------------------------------
|
||||
|
||||
Running our image seems complicated. We have to specify the whole command with
|
||||
``docker run``. Let's simplify it so the service starts automatically when the
|
||||
container starts.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker commit -run='{"Cmd": \
|
||||
["/bin/su", "postgres", "-c", "/usr/lib/postgresql/9.3/bin/postgres -D \
|
||||
/var/lib/postgresql/9.3/main -c \
|
||||
config_file=/etc/postgresql/9.3/main/postgresql.conf"], "PortSpecs": ["5432"]}' \
|
||||
<container_id> <your username>/postgresql
|
||||
|
||||
From now on, just type ``docker run <your username>/postgresql`` and
|
||||
PostgreSQL should automatically start.
|
||||
|
|
|
@ -9,109 +9,137 @@ Python Web App
|
|||
|
||||
.. include:: example_header.inc
|
||||
|
||||
The goal of this example is to show you how you can author your own
|
||||
Docker images using a parent image, making changes to it, and then
|
||||
saving the results as a new image. We will do that by making a simple
|
||||
hello Flask web application image.
|
||||
While using Dockerfiles is the preferred way to create maintainable
|
||||
and repeatable images, its useful to know how you can try things out
|
||||
and then commit your live changes to an image.
|
||||
|
||||
**Steps:**
|
||||
The goal of this example is to show you how you can modify your own
|
||||
Docker images by making changes to a running
|
||||
container, and then saving the results as a new image. We will do
|
||||
that by making a simple 'hello world' Flask web application image.
|
||||
|
||||
Download the initial image
|
||||
--------------------------
|
||||
|
||||
Download the ``shykes/pybuilder`` Docker image from the ``http://index.docker.io``
|
||||
registry.
|
||||
|
||||
This image contains a ``buildapp`` script to download the web app and then ``pip install``
|
||||
any required modules, and a ``runapp`` script that finds the ``app.py`` and runs it.
|
||||
|
||||
.. _`shykes/pybuilder`: https://github.com/shykes/pybuilder
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker pull shykes/pybuilder
|
||||
$ sudo docker pull shykes/pybuilder
|
||||
|
||||
We are downloading the ``shykes/pybuilder`` Docker image
|
||||
.. note:: This container was built with a very old version of docker
|
||||
(May 2013 - see `shykes/pybuilder`_ ), when the ``Dockerfile`` format was different,
|
||||
but the image can still be used now.
|
||||
|
||||
Interactively make some modifications
|
||||
-------------------------------------
|
||||
|
||||
We then start a new container running interactively using the image.
|
||||
First, we set a ``URL`` variable that points to a tarball of a simple
|
||||
helloflask web app, and then we run a command contained in the image called
|
||||
``buildapp``, passing it the ``$URL`` variable. The container is
|
||||
given a name ``pybuilder_run`` which we will use in the next steps.
|
||||
|
||||
While this example is simple, you could run any number of interactive commands,
|
||||
try things out, and then exit when you're done.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
URL=http://github.com/shykes/helloflask/archive/master.tar.gz
|
||||
$ sudo docker run -i -t -name pybuilder_run shykes/pybuilder bash
|
||||
|
||||
We set a ``URL`` variable that points to a tarball of a simple helloflask web app
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
BUILD_JOB=$(sudo docker run -d -t shykes/pybuilder:latest /usr/local/bin/buildapp $URL)
|
||||
|
||||
Inside of the ``shykes/pybuilder`` image there is a command called
|
||||
``buildapp``, we are running that command and passing the ``$URL`` variable
|
||||
from step 2 to it, and running the whole thing inside of a new
|
||||
container. The ``BUILD_JOB`` environment variable will be set with the new container ID.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker attach -sig-proxy=false $BUILD_JOB
|
||||
$$ URL=http://github.com/shykes/helloflask/archive/master.tar.gz
|
||||
$$ /usr/local/bin/buildapp $URL
|
||||
[...]
|
||||
$$ exit
|
||||
|
||||
While this container is running, we can attach to the new container to
|
||||
see what is going on. The flag ``--sig-proxy`` set as ``false`` allows you to connect and
|
||||
disconnect (Ctrl-C) to it without stopping the container.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker ps -a
|
||||
|
||||
List all Docker containers. If this container has already finished
|
||||
running, it will still be listed here.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
BUILD_IMG=$(sudo docker commit $BUILD_JOB _/builds/github.com/shykes/helloflask/master)
|
||||
Commit the container to create a new image
|
||||
------------------------------------------
|
||||
|
||||
Save the changes we just made in the container to a new image called
|
||||
``_/builds/github.com/hykes/helloflask/master`` and save the image ID in
|
||||
the ``BUILD_IMG`` variable name.
|
||||
``/builds/github.com/shykes/helloflask/master``. You now have 3 different
|
||||
ways to refer to the container: name ``pybuilder_run``, short-id ``c8b2e8228f11``, or
|
||||
long-id ``c8b2e8228f11b8b3e492cbf9a49923ae66496230056d61e07880dc74c5f495f9``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
WEB_WORKER=$(sudo docker run -d -p 5000 $BUILD_IMG /usr/local/bin/runapp)
|
||||
$ sudo docker commit pybuilder_run /builds/github.com/shykes/helloflask/master
|
||||
c8b2e8228f11b8b3e492cbf9a49923ae66496230056d61e07880dc74c5f495f9
|
||||
|
||||
|
||||
Run the new image to start the web worker
|
||||
-----------------------------------------
|
||||
|
||||
Use the new image to create a new container with
|
||||
network port 5000 mapped to a local port
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker run -d -p 5000 --name web_worker /builds/github.com/shykes/helloflask/master /usr/local/bin/runapp
|
||||
|
||||
|
||||
- **"docker run -d "** run a command in a new container. We pass "-d"
|
||||
so it runs as a daemon.
|
||||
- **"-p 5000"** the web app is going to listen on this port, so it
|
||||
must be mapped from the container to the host system.
|
||||
- **"$BUILD_IMG"** is the image we want to run the command inside of.
|
||||
- **/usr/local/bin/runapp** is the command which starts the web app.
|
||||
|
||||
Use the new image we just created and create a new container with
|
||||
network port 5000, and return the container ID and store in the
|
||||
``WEB_WORKER`` variable.
|
||||
|
||||
.. code-block:: bash
|
||||
View the container logs
|
||||
-----------------------
|
||||
|
||||
sudo docker logs $WEB_WORKER
|
||||
* Running on http://0.0.0.0:5000/
|
||||
|
||||
View the logs for the new container using the ``WEB_WORKER`` variable, and
|
||||
View the logs for the new ``web_worker`` container and
|
||||
if everything worked as planned you should see the line ``Running on
|
||||
http://0.0.0.0:5000/`` in the log output.
|
||||
|
||||
To exit the view without stopping the container, hit Ctrl-C, or open another
|
||||
terminal and continue with the example while watching the result in the logs.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
WEB_PORT=$(sudo docker port $WEB_WORKER 5000 | awk -F: '{ print $2 }')
|
||||
$ sudo docker logs -f web_worker
|
||||
* Running on http://0.0.0.0:5000/
|
||||
|
||||
|
||||
See the webapp output
|
||||
---------------------
|
||||
|
||||
Look up the public-facing port which is NAT-ed. Find the private port
|
||||
used by the container and store it inside of the ``WEB_PORT`` variable.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# install curl if necessary, then ...
|
||||
curl http://127.0.0.1:$WEB_PORT
|
||||
Hello world!
|
||||
|
||||
Access the web app using the ``curl`` binary. If everything worked as planned you
|
||||
should see the line ``Hello world!`` inside of your console.
|
||||
|
||||
**Video:**
|
||||
.. code-block:: bash
|
||||
|
||||
See the example in action
|
||||
$ WEB_PORT=$(sudo docker port web_worker 5000 | awk -F: '{ print $2 }')
|
||||
|
||||
.. raw:: html
|
||||
# install curl if necessary, then ...
|
||||
$ curl http://127.0.0.1:$WEB_PORT
|
||||
Hello world!
|
||||
|
||||
<iframe width="720" height="400" frameborder="0"
|
||||
sandbox="allow-same-origin allow-scripts"
|
||||
srcdoc="<body><script type="text/javascript"
|
||||
src="https://asciinema.org/a/2573.js"
|
||||
id="asciicast-2573" async></script></body>">
|
||||
</iframe>
|
||||
|
||||
Continue to :ref:`running_ssh_service`.
|
||||
Clean up example containers and images
|
||||
--------------------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker ps --all
|
||||
|
||||
List ``--all`` the Docker containers. If this container had already finished
|
||||
running, it will still be listed here with a status of 'Exit 0'.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker stop web_worker
|
||||
$ sudo docker rm web_worker pybuilder_run
|
||||
$ sudo docker rmi /builds/github.com/shykes/helloflask/master shykes/pybuilder:latest
|
||||
|
||||
And now stop the running web worker, and delete the containers, so that we can
|
||||
then delete the images that we used.
|
||||
|
||||
|
|
|
@ -67,14 +67,14 @@ Once inside our freshly created container we need to install Redis to get the
|
|||
apt-get -y install redis-server
|
||||
service redis-server stop
|
||||
|
||||
Now we can test the connection. Firstly, let's look at the available environmental
|
||||
variables in our web application container. We can use these to get the IP and port
|
||||
of our ``redis`` container.
|
||||
As we've used the ``--link redis:db`` option, Docker has created some environment
|
||||
variables in our web application container.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
env
|
||||
. . .
|
||||
env | grep DB_
|
||||
|
||||
# Should return something similar to this with your values
|
||||
DB_NAME=/violet_wolf/db
|
||||
DB_PORT_6379_TCP_PORT=6379
|
||||
DB_PORT=tcp://172.17.0.33:6379
|
||||
|
|
17
docs/sources/examples/running_ssh_service.Dockerfile
Normal file
17
docs/sources/examples/running_ssh_service.Dockerfile
Normal file
|
@ -0,0 +1,17 @@
|
|||
# sshd
|
||||
#
|
||||
# VERSION 0.0.1
|
||||
|
||||
FROM ubuntu
|
||||
MAINTAINER Thatcher R. Peskens "thatcher@dotcloud.com"
|
||||
|
||||
# make sure the package repository is up to date
|
||||
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y openssh-server
|
||||
RUN mkdir /var/run/sshd
|
||||
RUN echo 'root:screencast' |chpasswd
|
||||
|
||||
EXPOSE 22
|
||||
CMD /usr/sbin/sshd -D
|
|
@ -1,5 +1,5 @@
|
|||
:title: Running an SSH service
|
||||
:description: A screencast of installing and running an sshd service
|
||||
:description: Installing and running an sshd service
|
||||
:keywords: docker, example, package installation, networking
|
||||
|
||||
.. _running_ssh_service:
|
||||
|
@ -9,101 +9,41 @@ SSH Daemon Service
|
|||
|
||||
.. include:: example_header.inc
|
||||
|
||||
The following Dockerfile sets up an sshd service in a container that you can use
|
||||
to connect to and inspect other container's volumes, or to get quick access to a
|
||||
test container.
|
||||
|
||||
**Video:**
|
||||
.. literalinclude:: running_ssh_service.Dockerfile
|
||||
|
||||
I've created a little screencast to show how to create an SSHd service
|
||||
and connect to it. It is something like 11 minutes and not entirely
|
||||
smooth, but it gives you a good idea.
|
||||
|
||||
.. note::
|
||||
This screencast was created before Docker version 0.5.2, so the
|
||||
daemon is unprotected and available via a TCP port. When you run
|
||||
through the same steps in a newer version of Docker, you will
|
||||
need to add ``sudo`` in front of each ``docker`` command in order
|
||||
to reach the daemon over its protected Unix socket.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<iframe width="815" height="450" frameborder="0"
|
||||
sandbox="allow-same-origin allow-scripts"
|
||||
srcdoc="<body><script type="text/javascript"
|
||||
src="https://asciinema.org/a/2637.js"
|
||||
id="asciicast-2637" async></script></body>">
|
||||
</iframe>
|
||||
|
||||
You can also get this sshd container by using:
|
||||
Build the image using:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker pull dhrp/sshd
|
||||
$ sudo docker build -rm -t eg_sshd .
|
||||
|
||||
|
||||
The password is ``screencast``.
|
||||
|
||||
**Video's Transcription:**
|
||||
Then run it. You can then use ``docker port`` to find out what host port the container's
|
||||
port 22 is mapped to:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Hello! We are going to try and install openssh on a container and run it as a service
|
||||
# let's pull ubuntu to get a base ubuntu image.
|
||||
$ docker pull ubuntu
|
||||
# I had it so it was quick
|
||||
# now let's connect using -i for interactive and with -t for terminal
|
||||
# we execute /bin/bash to get a prompt.
|
||||
$ docker run -i -t ubuntu /bin/bash
|
||||
# yes! we are in!
|
||||
# now lets install openssh
|
||||
$ apt-get update
|
||||
$ apt-get install openssh-server
|
||||
# ok. lets see if we can run it.
|
||||
$ which sshd
|
||||
# we need to create privilege separation directory
|
||||
$ mkdir /var/run/sshd
|
||||
$ /usr/sbin/sshd
|
||||
$ exit
|
||||
# now let's commit it
|
||||
# which container was it?
|
||||
$ docker ps -a |more
|
||||
$ docker commit a30a3a2f2b130749995f5902f079dc6ad31ea0621fac595128ec59c6da07feea dhrp/sshd
|
||||
# I gave the name dhrp/sshd for the container
|
||||
# now we can run it again
|
||||
$ docker run -d dhrp/sshd /usr/sbin/sshd -D # D for daemon mode
|
||||
# is it running?
|
||||
$ docker ps
|
||||
# yes!
|
||||
# let's stop it
|
||||
$ docker stop 0ebf7cec294755399d063f4b1627980d4cbff7d999f0bc82b59c300f8536a562
|
||||
$ docker ps
|
||||
# and reconnect, but now open a port to it
|
||||
$ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D
|
||||
$ docker port b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 22
|
||||
# it has now given us a port to connect to
|
||||
# we have to connect using a public ip of our host
|
||||
$ hostname
|
||||
# *ifconfig* is deprecated, better use *ip addr show* now
|
||||
$ ifconfig
|
||||
$ ssh root@192.168.33.10 -p 49153
|
||||
# Ah! forgot to set root passwd
|
||||
$ docker commit b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 dhrp/sshd
|
||||
$ docker ps -a
|
||||
$ docker run -i -t dhrp/sshd /bin/bash
|
||||
$ passwd
|
||||
$ exit
|
||||
$ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd
|
||||
$ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D
|
||||
$ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22
|
||||
# *ifconfig* is deprecated, better use *ip addr show* now
|
||||
$ ifconfig
|
||||
$ ssh root@192.168.33.10 -p 49154
|
||||
# Thanks for watching, Thatcher thatcher@dotcloud.com
|
||||
|
||||
Update:
|
||||
-------
|
||||
$ sudo docker run -d -P -name test_sshd eg_sshd
|
||||
$ sudo docker port test_sshd 22
|
||||
0.0.0.0:49154
|
||||
|
||||
For Ubuntu 13.10 using stackbrew/ubuntu, you may need do these additional steps:
|
||||
And now you can ssh to port ``49154`` on the Docker daemon's host IP address
|
||||
(``ip address`` or ``ifconfig`` can tell you that):
|
||||
|
||||
1. change /etc/pam.d/sshd, pam_loginuid line 'required' to 'optional'
|
||||
2. echo LANG=\"en_US.UTF-8\" > /etc/default/locale
|
||||
.. code-block:: bash
|
||||
|
||||
$ ssh root@192.168.1.2 -p 49154
|
||||
# The password is ``screencast``.
|
||||
$$
|
||||
|
||||
Finally, clean up after your test by stopping and removing the container, and
|
||||
then removing the image.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker stop test_sshd
|
||||
$ sudo docker rm test_sshd
|
||||
$ sudo docker rmi eg_sshd
|
||||
|
|
|
@ -112,7 +112,7 @@ Once we've got a built image we can launch a container from it.
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker run -p 22 -p 80 -t -i <yourname>/supervisor
|
||||
sudo docker run -p 22 -p 80 -t -i <yourname>/supervisord
|
||||
2013-11-25 18:53:22,312 CRIT Supervisor running as root (no user in config file)
|
||||
2013-11-25 18:53:22,312 WARN Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
|
||||
2013-11-25 18:53:22,342 INFO supervisord started with pid 1
|
||||
|
|
|
@ -25,9 +25,9 @@ Does Docker run on Mac OS X or Windows?
|
|||
|
||||
Not at this time, Docker currently only runs on Linux, but you can
|
||||
use VirtualBox to run Docker in a virtual machine on your box, and
|
||||
get the best of both worlds. Check out the
|
||||
:ref:`macosx` and :ref:`windows` installation
|
||||
guides.
|
||||
get the best of both worlds. Check out the :ref:`macosx` and
|
||||
:ref:`windows` installation guides. The small Linux distribution boot2docker
|
||||
can be run inside virtual machines on these two operating systems.
|
||||
|
||||
How do containers compare to virtual machines?
|
||||
..............................................
|
||||
|
@ -183,10 +183,21 @@ Cloud:
|
|||
- Google Compute Engine
|
||||
- Rackspace
|
||||
|
||||
How do I report a security issue with Docker?
|
||||
.............................................
|
||||
|
||||
You can learn about the project's security policy `here <http://www.docker.io/security/>`_
|
||||
and report security issues to this `mailbox <mailto:security@docker.com>`_.
|
||||
|
||||
Why do I need to sign my commits to Docker with the DCO?
|
||||
........................................................
|
||||
|
||||
Please read `our blog post <http://blog.docker.io/2014/01/docker-code-contributions-require-developer-certificate-of-origin/>`_ on the introduction of the DCO.
|
||||
|
||||
Can I help by adding some questions and answers?
|
||||
................................................
|
||||
|
||||
Definitely! You can fork `the repo`_ and edit the documentation sources.
|
||||
Definitely! You can fork `the repo`_ and edit the documentation sources.
|
||||
|
||||
|
||||
Where can I find more answers?
|
||||
|
@ -210,5 +221,4 @@ Where can I find more answers?
|
|||
.. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
|
||||
.. _Join the conversation on Twitter: http://twitter.com/docker
|
||||
|
||||
|
||||
Looking for something else to read? Checkout the :ref:`hello_world` example.
|
||||
|
|
|
@ -17,13 +17,13 @@ Common use cases for Docker include:
|
|||
- Deploying and scaling databases and backend services in a service-oriented environment.
|
||||
- Building custom PaaS environments, either from scratch or as an extension of off-the-shelf platforms like OpenShift or Cloud Foundry.
|
||||
|
||||
Please note Docker is currently under heavy developement. It should not be used in production (yet).
|
||||
Please note Docker is currently under heavy development. It should not be used in production (yet).
|
||||
|
||||
For a high-level overview of Docker, please see the `Introduction
|
||||
<http://www.docker.io/learn_more/>`_. When you're ready to start working with
|
||||
Docker, we have a `quick start <http://www.docker.io/gettingstarted>`_
|
||||
and a more in-depth guide to :ref:`ubuntu_linux` and other
|
||||
:ref:`installation_list` paths including prebuilt binaries,
|
||||
Vagrant-created VMs, Rackspace and Amazon instances.
|
||||
Rackspace and Amazon instances.
|
||||
|
||||
Enough reading! :ref:`Try it out! <running_examples>`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
:title: Installation on Amazon EC2
|
||||
:description: Docker installation on Amazon EC2
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: amazon ec2, virtualization, cloud, docker, documentation, installation
|
||||
|
||||
Amazon EC2
|
||||
|
@ -10,8 +10,7 @@ Amazon EC2
|
|||
There are several ways to install Docker on AWS EC2:
|
||||
|
||||
* :ref:`amazonquickstart` or
|
||||
* :ref:`amazonstandard` or
|
||||
* :ref:`amazonvagrant`
|
||||
* :ref:`amazonstandard`
|
||||
|
||||
**You'll need an** `AWS account <http://aws.amazon.com/>`_ **first, of course.**
|
||||
|
||||
|
@ -73,112 +72,4 @@ running Ubuntu. Just follow Step 1 from :ref:`amazonquickstart` to
|
|||
pick an image (or use one of your own) and skip the step with the
|
||||
*User Data*. Then continue with the :ref:`ubuntu_linux` instructions.
|
||||
|
||||
.. _amazonvagrant:
|
||||
|
||||
Use Vagrant
|
||||
-----------
|
||||
|
||||
.. include:: install_unofficial.inc
|
||||
|
||||
And finally, if you prefer to work through Vagrant, you can install
|
||||
Docker that way too. Vagrant 1.1 or higher is required.
|
||||
|
||||
1. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
|
||||
2. Install the vagrant aws plugin
|
||||
|
||||
::
|
||||
|
||||
vagrant plugin install vagrant-aws
|
||||
|
||||
|
||||
3. Get the docker sources, this will give you the latest Vagrantfile.
|
||||
|
||||
::
|
||||
|
||||
git clone https://github.com/dotcloud/docker.git
|
||||
|
||||
|
||||
4. Check your AWS environment.
|
||||
|
||||
Create a keypair specifically for EC2, give it a name and save it
|
||||
to your disk. *I usually store these in my ~/.ssh/ folder*.
|
||||
|
||||
Check that your default security group has an inbound rule to
|
||||
accept SSH (port 22) connections.
|
||||
|
||||
5. Inform Vagrant of your settings
|
||||
|
||||
Vagrant will read your access credentials from your environment, so
|
||||
we need to set them there first. Make sure you have everything on
|
||||
amazon aws setup so you can (manually) deploy a new image to EC2.
|
||||
|
||||
Note that where possible these variables are the same as those honored by
|
||||
the ec2 api tools.
|
||||
::
|
||||
|
||||
export AWS_ACCESS_KEY=xxx
|
||||
export AWS_SECRET_KEY=xxx
|
||||
export AWS_KEYPAIR_NAME=xxx
|
||||
export SSH_PRIVKEY_PATH=xxx
|
||||
|
||||
export BOX_NAME=xxx
|
||||
export AWS_REGION=xxx
|
||||
export AWS_AMI=xxx
|
||||
export AWS_INSTANCE_TYPE=xxx
|
||||
|
||||
The required environment variables are:
|
||||
|
||||
* ``AWS_ACCESS_KEY`` - The API key used to make requests to AWS
|
||||
* ``AWS_SECRET_KEY`` - The secret key to make AWS API requests
|
||||
* ``AWS_KEYPAIR_NAME`` - The name of the keypair used for this EC2 instance
|
||||
* ``SSH_PRIVKEY_PATH`` - The path to the private key for the named
|
||||
keypair, for example ``~/.ssh/docker.pem``
|
||||
|
||||
There are a number of optional environment variables:
|
||||
|
||||
* ``BOX_NAME`` - The name of the vagrant box to use. Defaults to
|
||||
``ubuntu``.
|
||||
* ``AWS_REGION`` - The aws region to spawn the vm in. Defaults to
|
||||
``us-east-1``.
|
||||
* ``AWS_AMI`` - The aws AMI to start with as a base. This must be
|
||||
be an ubuntu 12.04 precise image. You must change this value if
|
||||
``AWS_REGION`` is set to a value other than ``us-east-1``.
|
||||
This is because AMIs are region specific. Defaults to ``ami-69f5a900``.
|
||||
* ``AWS_INSTANCE_TYPE`` - The aws instance type. Defaults to ``t1.micro``.
|
||||
|
||||
You can check if they are set correctly by doing something like
|
||||
|
||||
::
|
||||
|
||||
echo $AWS_ACCESS_KEY
|
||||
|
||||
6. Do the magic!
|
||||
|
||||
::
|
||||
|
||||
vagrant up --provider=aws
|
||||
|
||||
|
||||
If it stalls indefinitely on ``[default] Waiting for SSH to become
|
||||
available...``, Double check your default security zone on AWS
|
||||
includes rights to SSH (port 22) to your container.
|
||||
|
||||
If you have an advanced AWS setup, you might want to have a look at
|
||||
`vagrant-aws <https://github.com/mitchellh/vagrant-aws>`_.
|
||||
|
||||
7. Connect to your machine
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vagrant ssh
|
||||
|
||||
8. Your first command
|
||||
|
||||
Now you are in the VM, run docker
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker
|
||||
|
||||
|
||||
Continue with the :ref:`hello_world` example.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
:title: Installation on Arch Linux
|
||||
:description: Docker installation on Arch Linux.
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: arch linux, virtualization, docker, documentation, installation
|
||||
|
||||
.. _arch_linux:
|
||||
|
|
|
@ -26,10 +26,7 @@ Check runtime dependencies
|
|||
|
||||
To run properly, docker needs the following software to be installed at runtime:
|
||||
|
||||
- iproute2 version 3.5 or later (build after 2012-05-21), and
|
||||
specifically the "ip" utility
|
||||
- iptables version 1.4 or later
|
||||
- The LXC utility scripts (http://lxc.sourceforge.net) version 0.8 or later
|
||||
- Git version 1.7 or later
|
||||
- XZ Utils 4.9 or later
|
||||
|
||||
|
@ -41,7 +38,7 @@ Docker in daemon mode has specific kernel requirements. For details,
|
|||
check your distribution in :ref:`installation_list`.
|
||||
|
||||
Note that Docker also has a client mode, which can run on virtually
|
||||
any linux kernel (it even builds on OSX!).
|
||||
any Linux kernel (it even builds on OSX!).
|
||||
|
||||
|
||||
Get the docker binary:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:title: Requirements and Installation on Fedora
|
||||
:title: 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
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
:title: Installation on FrugalWare
|
||||
:description: Docker installation on FrugalWare.
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: frugalware linux, virtualization, docker, documentation, installation
|
||||
|
||||
.. _frugalware:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
:title: Installation on Gentoo Linux
|
||||
:description: Docker installation instructions and nuances for Gentoo Linux.
|
||||
:title: Installation on Gentoo
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: gentoo linux, virtualization, docker, documentation, installation
|
||||
|
||||
.. _gentoo_linux:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:title: Requirements and Installation on Mac OS X 10.6 Snow Leopard
|
||||
:title: Installation on Mac OS X 10.6 Snow Leopard
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Docker, Docker documentation, requirements, virtualbox, ssh, linux, os x, osx, mac
|
||||
|
||||
|
@ -39,7 +39,7 @@ boot2docker
|
|||
``docker`` daemon. It also takes care of the installation for the OS image
|
||||
that is used for the job.
|
||||
|
||||
.. _GitHub page: https://github.com/steeve/boot2docker
|
||||
.. _GitHub page: https://github.com/boot2docker/boot2docker
|
||||
|
||||
Open up a new terminal window, if you have not already.
|
||||
|
||||
|
@ -49,10 +49,10 @@ Run the following commands to get boot2docker:
|
|||
|
||||
# Enter the installation directory
|
||||
cd ~/bin
|
||||
|
||||
|
||||
# Get the file
|
||||
curl https://raw.github.com/steeve/boot2docker/master/boot2docker > boot2docker
|
||||
|
||||
curl https://raw.github.com/boot2docker/boot2docker/master/boot2docker > boot2docker
|
||||
|
||||
# Mark it executable
|
||||
chmod +x boot2docker
|
||||
|
||||
|
@ -67,13 +67,13 @@ Run the following commands to get it downloaded and set up:
|
|||
|
||||
# Get the file
|
||||
curl -o docker https://get.docker.io/builds/Darwin/x86_64/docker-latest
|
||||
|
||||
|
||||
# Mark it executable
|
||||
chmod +x docker
|
||||
|
||||
# Set the environment variable for the docker daemon
|
||||
export DOCKER_HOST=tcp://
|
||||
|
||||
export DOCKER_HOST=tcp://127.0.0.1:4243
|
||||
|
||||
# Copy the executable file
|
||||
sudo cp docker /usr/local/bin/
|
||||
|
||||
|
@ -94,7 +94,7 @@ Inside the ``~/bin`` directory, run the following commands:
|
|||
|
||||
# Run the VM (the docker daemon)
|
||||
./boot2docker up
|
||||
|
||||
|
||||
# To see all available commands:
|
||||
./boot2docker
|
||||
|
||||
|
@ -116,6 +116,21 @@ client just like any other application.
|
|||
# Git commit (server): c348c04
|
||||
# Go version (server): go1.2
|
||||
|
||||
Forwarding VM Port Range to Host
|
||||
--------------------------------
|
||||
|
||||
If we take the port range that docker uses by default with the -P option
|
||||
(49000-49900), and forward same range from host to vm, we'll be able to interact
|
||||
with our containers as if they were running locally:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# vm must be powered off
|
||||
for i in {49000..49900}; do
|
||||
VBoxManage modifyvm "boot2docker-vm" --natpf1 "tcp-port$i,tcp,,$i,,$i";
|
||||
VBoxManage modifyvm "boot2docker-vm" --natpf1 "udp-port$i,udp,,$i,,$i";
|
||||
done
|
||||
|
||||
SSH-ing The VM
|
||||
--------------
|
||||
|
||||
|
@ -138,7 +153,7 @@ boot2docker:
|
|||
|
||||
See the GitHub page for `boot2docker`_.
|
||||
|
||||
.. _boot2docker: https://github.com/steeve/boot2docker
|
||||
.. _boot2docker: https://github.com/boot2docker/boot2docker
|
||||
|
||||
If SSH complains about keys:
|
||||
----------------------------
|
||||
|
@ -147,6 +162,18 @@ If SSH complains about keys:
|
|||
|
||||
ssh-keygen -R '[localhost]:2022'
|
||||
|
||||
Upgrading to a newer release of boot2docker
|
||||
-------------------------------------------
|
||||
|
||||
To upgrade an initialised VM, you can use the following 3 commands. Your persistence
|
||||
disk will not be changed, so you won't lose your images and containers:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./boot2docker stop
|
||||
./boot2docker download
|
||||
./boot2docker start
|
||||
|
||||
About the way Docker works on Mac OS X:
|
||||
---------------------------------------
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
:title: Installation on openSUSE
|
||||
:description: Docker installation on openSUSE.
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: openSUSE, virtualbox, docker, documentation, installation
|
||||
|
||||
.. _openSUSE:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
:title: Rackspace Cloud Installation
|
||||
:description: Installing Docker on Ubuntu proviced by Rackspace
|
||||
:title: Installation on Rackspace Cloud
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Rackspace Cloud, installation, docker, linux, ubuntu
|
||||
|
||||
Rackspace Cloud
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:title: Requirements and Installation on Red Hat Enterprise Linux
|
||||
:title: Installation on Red Hat Enterprise Linux
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Docker, Docker documentation, requirements, linux, rhel, centos
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:title: Requirements and Installation on Ubuntu Linux
|
||||
:title: Installation on Ubuntu
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Docker, Docker documentation, requirements, virtualbox, vagrant, git, ssh, putty, cygwin, linux
|
||||
|
||||
|
@ -64,7 +64,7 @@ Installation
|
|||
an earlier version, you will need to follow them again.
|
||||
|
||||
Docker is available as a Debian package, which makes installation
|
||||
easy. **See the :ref:`installmirrors` section below if you are not in
|
||||
easy. **See the** :ref:`installmirrors` **section below if you are not in
|
||||
the United States.** Other sources of the Debian packages may be
|
||||
faster for you to install.
|
||||
|
||||
|
@ -182,9 +182,12 @@ 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 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.
|
||||
client commands. As of 0.9.0, you can specify that a group other than ``docker``
|
||||
should own the Unix socket with the ``-G`` option.
|
||||
|
||||
.. warning:: The *docker* group (or the group specified with ``-G``) is
|
||||
root-equivalent.
|
||||
|
||||
.. warning:: The *docker* group is root-equivalent.
|
||||
|
||||
**Example:**
|
||||
|
||||
|
@ -217,15 +220,35 @@ To install the latest version of docker, use the standard ``apt-get`` method:
|
|||
# install the latest
|
||||
sudo apt-get install lxc-docker
|
||||
|
||||
Memory and Swap Accounting
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If want to enable memory and swap accounting, you must add the following
|
||||
command-line parameters to your kernel::
|
||||
|
||||
cgroup_enable=memory swapaccount=1
|
||||
|
||||
On systems using GRUB (which is the default for Ubuntu), you can add those
|
||||
parameters by editing ``/etc/default/grub`` and extending
|
||||
``GRUB_CMDLINE_LINUX``. Look for the following line::
|
||||
|
||||
GRUB_CMDLINE_LINUX=""
|
||||
|
||||
And replace it by the following one::
|
||||
|
||||
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
|
||||
|
||||
Then run ``update-grub``, and reboot.
|
||||
|
||||
Troubleshooting
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
On Linux Mint, the ``cgroups-lite`` package is not installed by default.
|
||||
On Linux Mint, the ``cgroup-lite`` package is not installed by default.
|
||||
Before Docker will work correctly, you will need to install this via:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt-get update && sudo apt-get install cgroups-lite
|
||||
sudo apt-get update && sudo apt-get install cgroup-lite
|
||||
|
||||
.. _ufw:
|
||||
|
||||
|
@ -261,6 +284,64 @@ incoming connections on the Docker port (default 4243):
|
|||
|
||||
.. _installmirrors:
|
||||
|
||||
Docker and local DNS server warnings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Systems which are running Ubuntu or an Ubuntu derivative on the desktop will
|
||||
use `127.0.0.1` as the default nameserver in `/etc/resolv.conf`. NetworkManager
|
||||
sets up dnsmasq to use the real DNS servers of the connection and sets up
|
||||
`nameserver 127.0.0.1` in `/etc/resolv.conf`.
|
||||
|
||||
When starting containers on these desktop machines, users will see a warning:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
WARNING: Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : [8.8.8.8 8.8.4.4]
|
||||
|
||||
This warning is shown because the containers can't use the local DNS nameserver
|
||||
and Docker will default to using an external nameserver.
|
||||
|
||||
This can be worked around by specifying a DNS server to be used by the Docker
|
||||
daemon for the containers:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo nano /etc/default/docker
|
||||
---
|
||||
# Add:
|
||||
DOCKER_OPTS="-dns 8.8.8.8"
|
||||
# 8.8.8.8 could be replaced with a local DNS server, such as 192.168.1.1
|
||||
# multiple DNS servers can be specified: -dns 8.8.8.8 -dns 192.168.1.1
|
||||
|
||||
The Docker daemon has to be restarted:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo restart docker
|
||||
|
||||
.. warning:: If you're doing this on a laptop which connects to various networks, make sure to choose a public DNS server.
|
||||
|
||||
An alternative solution involves disabling dnsmasq in NetworkManager by
|
||||
following these steps:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo nano /etc/NetworkManager/NetworkManager.conf
|
||||
----
|
||||
# Change:
|
||||
dns=dnsmasq
|
||||
# to
|
||||
#dns=dnsmasq
|
||||
|
||||
NetworkManager and Docker need to be restarted afterwards:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo restart network-manager
|
||||
sudo restart docker
|
||||
|
||||
.. warning:: This might make DNS resolution slower on some networks.
|
||||
|
||||
Mirrors
|
||||
^^^^^^^
|
||||
|
||||
|
|
|
@ -1,223 +1,72 @@
|
|||
:title: Requirements and Installation on Windows
|
||||
:description: Docker's tutorial to run docker on Windows
|
||||
:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
|
||||
:title: Installation on Windows
|
||||
:description: Please note this project is currently under heavy development. It should not be used in production.
|
||||
:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, boot2docker
|
||||
|
||||
.. _windows:
|
||||
|
||||
Installing Docker on Windows
|
||||
============================
|
||||
Windows
|
||||
=======
|
||||
|
||||
Docker can run on Windows using a VM like VirtualBox. You then run
|
||||
Linux within the VM.
|
||||
Docker can run on Windows using a virtualization platform like VirtualBox. A Linux
|
||||
distribution is run inside a virtual machine and that's where Docker will run.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
.. include:: install_header.inc
|
||||
|
||||
.. include:: install_unofficial.inc
|
||||
1. Install virtualbox from https://www.virtualbox.org - or follow this `tutorial <http://www.slideshare.net/julienbarbier42/install-virtualbox-on-windows-7>`_.
|
||||
|
||||
1. Install virtualbox from https://www.virtualbox.org - or follow this tutorial__
|
||||
2. Download the latest boot2docker.iso from https://github.com/boot2docker/boot2docker/releases.
|
||||
|
||||
.. __: http://www.slideshare.net/julienbarbier42/install-virtualbox-on-windows-7
|
||||
3. Start VirtualBox.
|
||||
|
||||
2. Install vagrant from http://www.vagrantup.com - or follow this tutorial__
|
||||
4. Create a new Virtual machine with the following settings:
|
||||
|
||||
.. __: http://www.slideshare.net/julienbarbier42/install-vagrant-on-windows-7
|
||||
- `Name: boot2docker`
|
||||
- `Type: Linux`
|
||||
- `Version: Linux 2.6 (64 bit)`
|
||||
- `Memory size: 1024 MB`
|
||||
- `Hard drive: Do not add a virtual hard drive`
|
||||
|
||||
3. Install git with ssh from http://git-scm.com/downloads - or follow this tutorial__
|
||||
5. Open the settings of the virtual machine:
|
||||
|
||||
.. __: http://www.slideshare.net/julienbarbier42/install-git-with-ssh-on-windows-7
|
||||
5.1. go to Storage
|
||||
|
||||
5.2. click the empty slot below `Controller: IDE`
|
||||
|
||||
We recommend having at least 2Gb of free disk space and 2Gb of RAM (or more).
|
||||
5.3. click the disc icon on the right of `IDE Secondary Master`
|
||||
|
||||
Opening a command prompt
|
||||
------------------------
|
||||
5.4. click `Choose a virtual CD/DVD disk file`
|
||||
|
||||
First open a cmd prompt. Press Windows key and then press “R”
|
||||
key. This will open the RUN dialog box for you. Type “cmd” and press
|
||||
Enter. Or you can click on Start, type “cmd” in the “Search programs
|
||||
and files” field, and click on cmd.exe.
|
||||
6. Browse to the path where you've saved the `boot2docker.iso`, select the `boot2docker.iso` and click open.
|
||||
|
||||
.. image:: images/win/_01.gif
|
||||
:alt: Git install
|
||||
:align: center
|
||||
7. Click OK on the Settings dialog to save the changes and close the window.
|
||||
|
||||
This should open a cmd prompt window.
|
||||
8. Start the virtual machine by clicking the green start button.
|
||||
|
||||
.. image:: images/win/_02.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
|
||||
Alternatively, you can also use a Cygwin terminal, or Git Bash (or any
|
||||
other command line program you are usually using). The next steps
|
||||
would be the same.
|
||||
|
||||
.. _launch_ubuntu:
|
||||
|
||||
Launch an Ubuntu virtual server
|
||||
-------------------------------
|
||||
|
||||
Let’s download and run an Ubuntu image with docker binaries already
|
||||
installed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/dotcloud/docker.git
|
||||
cd docker
|
||||
vagrant up
|
||||
|
||||
.. image:: images/win/run_02_.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
|
||||
Congratulations! You are running an Ubuntu server with docker
|
||||
installed on it. You do not see it though, because it is running in
|
||||
the background.
|
||||
|
||||
Log onto your Ubuntu server
|
||||
---------------------------
|
||||
|
||||
Let’s log into your Ubuntu server now. To do so you have two choices:
|
||||
|
||||
- Use Vagrant on Windows command prompt OR
|
||||
- Use SSH
|
||||
|
||||
Using Vagrant on Windows Command Prompt
|
||||
```````````````````````````````````````
|
||||
|
||||
Run the following command
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vagrant ssh
|
||||
|
||||
You may see an error message starting with “`ssh` executable not
|
||||
found”. In this case it means that you do not have SSH in your
|
||||
PATH. If you do not have SSH in your PATH you can set it up with the
|
||||
“set” command. For instance, if your ssh.exe is in the folder named
|
||||
“C:\Program Files (x86)\Git\bin”, then you can run the following
|
||||
command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
set PATH=%PATH%;C:\Program Files (x86)\Git\bin
|
||||
|
||||
.. image:: images/win/run_03.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
|
||||
Using SSH
|
||||
`````````
|
||||
|
||||
First step is to get the IP and port of your Ubuntu server. Simply run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vagrant ssh-config
|
||||
|
||||
You should see an output with HostName and Port information. In this
|
||||
example, HostName is 127.0.0.1 and port is 2222. And the User is
|
||||
“vagrant”. The password is not shown, but it is also “vagrant”.
|
||||
|
||||
.. image:: images/win/ssh-config.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
|
||||
You can now use this information for connecting via SSH to your
|
||||
server. To do so you can:
|
||||
|
||||
- Use putty.exe OR
|
||||
- Use SSH from a terminal
|
||||
|
||||
Use putty.exe
|
||||
'''''''''''''
|
||||
|
||||
You can download putty.exe from this page
|
||||
http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html Launch
|
||||
putty.exe and simply enter the information you got from last step.
|
||||
|
||||
.. image:: images/win/putty.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
|
||||
Open, and enter user = vagrant and password = vagrant.
|
||||
|
||||
.. image:: images/win/putty_2.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
|
||||
SSH from a terminal
|
||||
'''''''''''''''''''
|
||||
|
||||
You can also run this command on your favorite terminal (windows
|
||||
prompt, cygwin, git-bash, …). Make sure to adapt the IP and port from
|
||||
what you got from the vagrant ssh-config command.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
ssh vagrant@127.0.0.1 –p 2222
|
||||
|
||||
Enter user = vagrant and password = vagrant.
|
||||
|
||||
.. image:: images/win/cygwin.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
|
||||
Congratulations, you are now logged onto your Ubuntu Server, running
|
||||
on top of your Windows machine !
|
||||
9. The boot2docker virtual machine should boot now.
|
||||
|
||||
Running Docker
|
||||
--------------
|
||||
|
||||
First you have to be root in order to run docker. Simply run the
|
||||
following command:
|
||||
boot2docker will log you in automatically so you can start using Docker right
|
||||
away.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo su
|
||||
|
||||
You are now ready for the docker’s “hello world” example. Run
|
||||
Let's try the “hello world” example. Run
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run busybox echo hello world
|
||||
|
||||
.. image:: images/win/run_04.gif
|
||||
:alt: run docker
|
||||
:align: center
|
||||
This will download the small busybox image and print hello world.
|
||||
|
||||
All done!
|
||||
|
||||
Now you can continue with the :ref:`hello_world` example.
|
||||
Observations
|
||||
------------
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
Persistent storage
|
||||
``````````````````
|
||||
|
||||
VM does not boot
|
||||
````````````````
|
||||
|
||||
.. image:: images/win/ts_go_bios.JPG
|
||||
|
||||
If you run into this error message "The VM failed to remain in the
|
||||
'running' state while attempting to boot", please check that your
|
||||
computer has virtualization technology available and activated by
|
||||
going to the BIOS. Here's an example for an HP computer (System
|
||||
configuration / Device configuration)
|
||||
|
||||
.. image:: images/win/hp_bios_vm.JPG
|
||||
|
||||
On some machines the BIOS menu can only be accessed before startup.
|
||||
To access BIOS in this scenario you should restart your computer and
|
||||
press ESC/Enter when prompted to access the boot and BIOS controls. Typically
|
||||
the option to allow virtualization is contained within the BIOS/Security menu.
|
||||
|
||||
Docker is not installed
|
||||
```````````````````````
|
||||
|
||||
.. image:: images/win/ts_no_docker.JPG
|
||||
|
||||
If you run into this error message "The program 'docker' is currently
|
||||
not installed", try deleting the docker folder and restart from
|
||||
:ref:`launch_ubuntu`
|
||||
The virtual machine created above lacks any persistent data storage. All images
|
||||
and containers will be lost when shutting down or rebooting the VM.
|
||||
|
|
|
@ -3,3 +3,4 @@ This directory holds the authoritative specifications of APIs defined and implem
|
|||
* The remote API by which a docker node can be queried over HTTP
|
||||
* The registry API by which a docker node can download and upload container images for storage and sharing
|
||||
* The index search API by which a docker node can search the public index for images to download
|
||||
* The docker.io OAuth and accounts API which 3rd party services can use to access account information
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 175 KiB |
308
docs/sources/reference/api/docker_io_accounts_api.rst
Normal file
308
docs/sources/reference/api/docker_io_accounts_api.rst
Normal file
|
@ -0,0 +1,308 @@
|
|||
:title: docker.io Accounts API
|
||||
:description: API Documentation for docker.io accounts.
|
||||
:keywords: API, Docker, accounts, REST, documentation
|
||||
|
||||
|
||||
======================
|
||||
docker.io Accounts API
|
||||
======================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
|
||||
1. Endpoints
|
||||
============
|
||||
|
||||
|
||||
1.1 Get a single user
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:get:: /api/v1.1/users/:username/
|
||||
|
||||
Get profile info for the specified user.
|
||||
|
||||
:param username: username of the user whose profile info is being requested.
|
||||
|
||||
:reqheader Authorization: required authentication credentials of either type HTTP Basic or OAuth Bearer Token.
|
||||
|
||||
:statuscode 200: success, user data returned.
|
||||
:statuscode 401: authentication error.
|
||||
:statuscode 403: permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have ``profile_read`` scope.
|
||||
:statuscode 404: the specified username does not exist.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1.1/users/janedoe/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Accept: application/json
|
||||
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"username": "janedoe",
|
||||
"url": "",
|
||||
"date_joined": "2014-02-12T17:58:01.431312Z",
|
||||
"type": "User",
|
||||
"full_name": "Jane Doe",
|
||||
"location": "San Francisco, CA",
|
||||
"company": "Success, Inc.",
|
||||
"profile_url": "https://docker.io/",
|
||||
"gravatar_email": "jane.doe+gravatar@example.com",
|
||||
"email": "jane.doe@example.com",
|
||||
"is_active": true
|
||||
}
|
||||
|
||||
|
||||
1.2 Update a single user
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:patch:: /api/v1.1/users/:username/
|
||||
|
||||
Update profile info for the specified user.
|
||||
|
||||
:param username: username of the user whose profile info is being updated.
|
||||
|
||||
:jsonparam string full_name: (optional) the new name of the user.
|
||||
:jsonparam string location: (optional) the new location.
|
||||
:jsonparam string company: (optional) the new company of the user.
|
||||
:jsonparam string profile_url: (optional) the new profile url.
|
||||
:jsonparam string gravatar_email: (optional) the new Gravatar email address.
|
||||
|
||||
:reqheader Authorization: required authentication credentials of either type HTTP Basic or OAuth Bearer Token.
|
||||
:reqheader Content-Type: MIME Type of post data. JSON, url-encoded form data, etc.
|
||||
|
||||
:statuscode 200: success, user data updated.
|
||||
:statuscode 400: post data validation error.
|
||||
:statuscode 401: authentication error.
|
||||
:statuscode 403: permission error, authenticated user must be the user whose data is being updated, OAuth access tokens must have ``profile_write`` scope.
|
||||
:statuscode 404: the specified username does not exist.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1.1/users/janedoe/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Accept: application/json
|
||||
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
|
||||
|
||||
{
|
||||
"location": "Private Island",
|
||||
"profile_url": "http://janedoe.com/",
|
||||
"company": "Retired",
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"username": "janedoe",
|
||||
"url": "",
|
||||
"date_joined": "2014-02-12T17:58:01.431312Z",
|
||||
"type": "User",
|
||||
"full_name": "Jane Doe",
|
||||
"location": "Private Island",
|
||||
"company": "Retired",
|
||||
"profile_url": "http://janedoe.com/",
|
||||
"gravatar_email": "jane.doe+gravatar@example.com",
|
||||
"email": "jane.doe@example.com",
|
||||
"is_active": true
|
||||
}
|
||||
|
||||
|
||||
1.3 List email addresses for a user
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:get:: /api/v1.1/users/:username/emails/
|
||||
|
||||
List email info for the specified user.
|
||||
|
||||
:param username: username of the user whose profile info is being updated.
|
||||
|
||||
:reqheader Authorization: required authentication credentials of either type HTTP Basic or OAuth Bearer Token
|
||||
|
||||
:statuscode 200: success, user data updated.
|
||||
:statuscode 401: authentication error.
|
||||
:statuscode 403: permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have ``email_read`` scope.
|
||||
:statuscode 404: the specified username does not exist.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1.1/users/janedoe/emails/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Accept: application/json
|
||||
Authorization: Bearer zAy0BxC1wDv2EuF3tGs4HrI6qJp6KoL7nM
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"email": "jane.doe@example.com",
|
||||
"verified": true,
|
||||
"primary": true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
1.4 Add email address for a user
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:post:: /api/v1.1/users/:username/emails/
|
||||
|
||||
Add a new email address to the specified user's account. The email address
|
||||
must be verified separately, a confirmation email is not automatically sent.
|
||||
|
||||
:jsonparam string email: email address to be added.
|
||||
|
||||
:reqheader Authorization: required authentication credentials of either type HTTP Basic or OAuth Bearer Token.
|
||||
:reqheader Content-Type: MIME Type of post data. JSON, url-encoded form data, etc.
|
||||
|
||||
:statuscode 201: success, new email added.
|
||||
:statuscode 400: data validation error.
|
||||
:statuscode 401: authentication error.
|
||||
:statuscode 403: permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have ``email_write`` scope.
|
||||
:statuscode 404: the specified username does not exist.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1.1/users/janedoe/emails/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer zAy0BxC1wDv2EuF3tGs4HrI6qJp6KoL7nM
|
||||
|
||||
{
|
||||
"email": "jane.doe+other@example.com"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "jane.doe+other@example.com",
|
||||
"verified": false,
|
||||
"primary": false
|
||||
}
|
||||
|
||||
|
||||
1.5 Update an email address for a user
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:patch:: /api/v1.1/users/:username/emails/
|
||||
|
||||
Update an email address for the specified user to either verify an email
|
||||
address or set it as the primary email for the user. You cannot use this
|
||||
endpoint to un-verify an email address. You cannot use this endpoint to
|
||||
unset the primary email, only set another as the primary.
|
||||
|
||||
:param username: username of the user whose email info is being updated.
|
||||
|
||||
:jsonparam string email: the email address to be updated.
|
||||
:jsonparam boolean verified: (optional) whether the email address is verified, must be ``true`` or absent.
|
||||
:jsonparam boolean primary: (optional) whether to set the email address as the primary email, must be ``true`` or absent.
|
||||
|
||||
:reqheader Authorization: required authentication credentials of either type HTTP Basic or OAuth Bearer Token.
|
||||
:reqheader Content-Type: MIME Type of post data. JSON, url-encoded form data, etc.
|
||||
|
||||
:statuscode 200: success, user's email updated.
|
||||
:statuscode 400: data validation error.
|
||||
:statuscode 401: authentication error.
|
||||
:statuscode 403: permission error, authenticated user must be the user whose data is being updated, OAuth access tokens must have ``email_write`` scope.
|
||||
:statuscode 404: the specified username or email address does not exist.
|
||||
|
||||
**Example request**:
|
||||
|
||||
Once you have independently verified an email address.
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1.1/users/janedoe/emails/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Accept: application/json
|
||||
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
|
||||
|
||||
{
|
||||
"email": "jane.doe+other@example.com",
|
||||
"verified": true,
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "jane.doe+other@example.com",
|
||||
"verified": true,
|
||||
"primary": false
|
||||
}
|
||||
|
||||
|
||||
1.6 Delete email address for a user
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. http:delete:: /api/v1.1/users/:username/emails/
|
||||
|
||||
Delete an email address from the specified user's account. You cannot
|
||||
delete a user's primary email address.
|
||||
|
||||
:jsonparam string email: email address to be deleted.
|
||||
|
||||
:reqheader Authorization: required authentication credentials of either type HTTP Basic or OAuth Bearer Token.
|
||||
:reqheader Content-Type: MIME Type of post data. JSON, url-encoded form data, etc.
|
||||
|
||||
:statuscode 204: success, email address removed.
|
||||
:statuscode 400: validation error.
|
||||
:statuscode 401: authentication error.
|
||||
:statuscode 403: permission error, authenticated user must be the user whose data is being requested, OAuth access tokens must have ``email_write`` scope.
|
||||
:statuscode 404: the specified username or email address does not exist.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1.1/users/janedoe/emails/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer zAy0BxC1wDv2EuF3tGs4HrI6qJp6KoL7nM
|
||||
|
||||
{
|
||||
"email": "jane.doe+other@example.com"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 204 NO CONTENT
|
||||
Content-Length: 0
|
253
docs/sources/reference/api/docker_io_oauth_api.rst
Normal file
253
docs/sources/reference/api/docker_io_oauth_api.rst
Normal file
|
@ -0,0 +1,253 @@
|
|||
:title: docker.io OAuth API
|
||||
:description: API Documentation for docker.io's OAuth flow.
|
||||
:keywords: API, Docker, oauth, REST, documentation
|
||||
|
||||
|
||||
===================
|
||||
docker.io OAuth API
|
||||
===================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
|
||||
1. Brief introduction
|
||||
=====================
|
||||
|
||||
Some docker.io API requests will require an access token to authenticate. To
|
||||
get an access token for a user, that user must first grant your application
|
||||
access to their docker.io account. In order for them to grant your application
|
||||
access you must first register your application.
|
||||
|
||||
Before continuing, we encourage you to familiarize yourself with
|
||||
`The OAuth 2.0 Authorization Framework <http://tools.ietf.org/html/rfc6749>`_.
|
||||
|
||||
*Also note that all OAuth interactions must take place over https connections*
|
||||
|
||||
|
||||
2. Register Your Application
|
||||
============================
|
||||
|
||||
You will need to register your application with docker.io before users will
|
||||
be able to grant your application access to their account information. We
|
||||
are currently only allowing applications selectively. To request registration
|
||||
of your application send an email to support-accounts@docker.com with the
|
||||
following information:
|
||||
|
||||
- The name of your application
|
||||
- A description of your application and the service it will provide
|
||||
to docker.io users.
|
||||
- A callback URI that we will use for redirecting authorization requests to
|
||||
your application. These are used in the step of getting an Authorization
|
||||
Code. The domain name of the callback URI will be visible to the user when
|
||||
they are requested to authorize your application.
|
||||
|
||||
When your application is approved you will receive a response from the
|
||||
docker.io team with your ``client_id`` and ``client_secret`` which your
|
||||
application will use in the steps of getting an Authorization Code and getting
|
||||
an Access Token.
|
||||
|
||||
|
||||
3. Endpoints
|
||||
============
|
||||
|
||||
3.1 Get an Authorization Code
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once You have registered you are ready to start integrating docker.io accounts
|
||||
into your application! The process is usually started by a user following a
|
||||
link in your application to an OAuth Authorization endpoint.
|
||||
|
||||
.. http:get:: /api/v1.1/o/authorize/
|
||||
|
||||
Request that a docker.io user authorize your application. If the user is
|
||||
not already logged in, they will be prompted to login. The user is then
|
||||
presented with a form to authorize your application for the requested
|
||||
access scope. On submission, the user will be redirected to the specified
|
||||
``redirect_uri`` with an Authorization Code.
|
||||
|
||||
:query client_id: The ``client_id`` given to your application at
|
||||
registration.
|
||||
:query response_type: MUST be set to ``code``. This specifies that you
|
||||
would like an Authorization Code returned.
|
||||
:query redirect_uri: The URI to redirect back to after the user has
|
||||
authorized your application. If omitted, the first of your registered
|
||||
``response_uris`` is used. If included, it must be one of the URIs
|
||||
which were submitted when registering your application.
|
||||
:query scope: The extent of access permissions you are requesting.
|
||||
Currently, the scope options are ``profile_read``, ``profile_write``,
|
||||
``email_read``, and ``email_write``. Scopes must be separated by a
|
||||
space. If omitted, the default scopes ``profile_read email_read`` are
|
||||
used.
|
||||
:query state: (Recommended) Used by your application to maintain state
|
||||
between the authorization request and callback to protect against CSRF
|
||||
attacks.
|
||||
|
||||
**Example Request**
|
||||
|
||||
Asking the user for authorization.
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1.1/o/authorize/?client_id=TestClientID&response_type=code&redirect_uri=https%3A//my.app/auth_complete/&scope=profile_read%20email_read&state=abc123 HTTP/1.1
|
||||
Host: www.docker.io
|
||||
|
||||
**Authorization Page**
|
||||
|
||||
When the user follows a link, making the above GET request, they will be
|
||||
asked to login to their docker.io account if they are not already and then
|
||||
be presented with the following authorization prompt which asks the user
|
||||
to authorize your application with a description of the requested scopes.
|
||||
|
||||
.. image:: _static/io_oauth_authorization_page.png
|
||||
|
||||
Once the user allows or denies your Authorization Request the user will be
|
||||
redirected back to your application. Included in that request will be the
|
||||
following query parameters:
|
||||
|
||||
``code``
|
||||
The Authorization code generated by the docker.io authorization server.
|
||||
Present it again to request an Access Token. This code expires in 60
|
||||
seconds.
|
||||
|
||||
``state``
|
||||
If the ``state`` parameter was present in the authorization request this
|
||||
will be the exact value received from that request.
|
||||
|
||||
``error``
|
||||
An error message in the event of the user denying the authorization or
|
||||
some other kind of error with the request.
|
||||
|
||||
|
||||
3.2 Get an Access Token
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once the user has authorized your application, a request will be made to your
|
||||
application's specified ``redirect_uri`` which includes a ``code`` parameter
|
||||
that you must then use to get an Access Token.
|
||||
|
||||
.. http:post:: /api/v1.1/o/token/
|
||||
|
||||
Submit your newly granted Authorization Code and your application's
|
||||
credentials to receive an Access Token and Refresh Token. The code is valid
|
||||
for 60 seconds and cannot be used more than once.
|
||||
|
||||
:reqheader Authorization: HTTP basic authentication using your
|
||||
application's ``client_id`` and ``client_secret``
|
||||
|
||||
:form grant_type: MUST be set to ``authorization_code``
|
||||
:form code: The authorization code received from the user's redirect
|
||||
request.
|
||||
:form redirect_uri: The same ``redirect_uri`` used in the authentication
|
||||
request.
|
||||
|
||||
**Example Request**
|
||||
|
||||
Using an authorization code to get an access token.
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1.1/o/token/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Authorization: Basic VGVzdENsaWVudElEOlRlc3RDbGllbnRTZWNyZXQ=
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"grant_type": "code",
|
||||
"code": "YXV0aG9yaXphdGlvbl9jb2Rl",
|
||||
"redirect_uri": "https://my.app/auth_complete/"
|
||||
}
|
||||
|
||||
**Example Response**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
{
|
||||
"username": "janedoe",
|
||||
"user_id": 42,
|
||||
"access_token": "t6k2BqgRw59hphQBsbBoPPWLqu6FmS",
|
||||
"expires_in": 15552000,
|
||||
"token_type": "Bearer",
|
||||
"scope": "profile_read email_read",
|
||||
"refresh_token": "hJDhLH3cfsUrQlT4MxA6s8xAFEqdgc"
|
||||
}
|
||||
|
||||
In the case of an error, there will be a non-200 HTTP Status and and data
|
||||
detailing the error.
|
||||
|
||||
|
||||
3.3 Refresh a Token
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once the Access Token expires you can use your ``refresh_token`` to have
|
||||
docker.io issue your application a new Access Token, if the user has not
|
||||
revoked access from your application.
|
||||
|
||||
.. http:post:: /api/v1.1/o/token/
|
||||
|
||||
Submit your ``refresh_token`` and application's credentials to receive a
|
||||
new Access Token and Refresh Token. The ``refresh_token`` can be used
|
||||
only once.
|
||||
|
||||
:reqheader Authorization: HTTP basic authentication using your
|
||||
application's ``client_id`` and ``client_secret``
|
||||
|
||||
:form grant_type: MUST be set to ``refresh_token``
|
||||
:form refresh_token: The ``refresh_token`` which was issued to your
|
||||
application.
|
||||
:form scope: (optional) The scope of the access token to be returned.
|
||||
Must not include any scope not originally granted by the user and if
|
||||
omitted is treated as equal to the scope originally granted.
|
||||
|
||||
**Example Request**
|
||||
|
||||
Refreshing an access token.
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1.1/o/token/ HTTP/1.1
|
||||
Host: www.docker.io
|
||||
Authorization: Basic VGVzdENsaWVudElEOlRlc3RDbGllbnRTZWNyZXQ=
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": "hJDhLH3cfsUrQlT4MxA6s8xAFEqdgc",
|
||||
}
|
||||
|
||||
**Example Response**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
{
|
||||
"username": "janedoe",
|
||||
"user_id": 42,
|
||||
"access_token": "t6k2BqgRw59hphQBsbBoPPWLqu6FmS",
|
||||
"expires_in": 15552000,
|
||||
"token_type": "Bearer",
|
||||
"scope": "profile_read email_read",
|
||||
"refresh_token": "hJDhLH3cfsUrQlT4MxA6s8xAFEqdgc"
|
||||
}
|
||||
|
||||
In the case of an error, there will be a non-200 HTTP Status and and data
|
||||
detailing the error.
|
||||
|
||||
|
||||
4. Use an Access Token with the API
|
||||
===================================
|
||||
|
||||
Many of the docker.io API requests will require a Authorization request header
|
||||
field. Simply ensure you add this header with "Bearer <``access_token``>":
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1.1/resource HTTP/1.1
|
||||
Host: docker.io
|
||||
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
|
|
@ -2,7 +2,7 @@
|
|||
:description: API Documentation for Docker
|
||||
:keywords: API, Docker, rcli, REST, documentation
|
||||
|
||||
.. COMMENT use http://pythonhosted.org/sphinxcontrib-httpdomain/ to
|
||||
.. COMMENT use https://pythonhosted.org/sphinxcontrib-httpdomain/ to
|
||||
.. document the REST API.
|
||||
|
||||
=================
|
||||
|
@ -26,15 +26,36 @@ Docker Remote API
|
|||
2. Versions
|
||||
===========
|
||||
|
||||
The current version of the API is 1.9
|
||||
The current version of the API is 1.10
|
||||
|
||||
Calling /images/<name>/insert is the same as calling
|
||||
/v1.9/images/<name>/insert
|
||||
/v1.10/images/<name>/insert
|
||||
|
||||
You can still call an old version of the api using
|
||||
/v1.0/images/<name>/insert
|
||||
|
||||
|
||||
v1.10
|
||||
*****
|
||||
|
||||
Full Documentation
|
||||
------------------
|
||||
|
||||
:doc:`docker_remote_api_v1.10`
|
||||
|
||||
What's new
|
||||
----------
|
||||
|
||||
.. http:delete:: /images/(name)
|
||||
|
||||
**New!** You can now use the force parameter to force delete of an image, even if it's
|
||||
tagged in multiple repositories.
|
||||
|
||||
.. http:delete:: /containers/(id)
|
||||
|
||||
**New!** You can now use the force paramter to force delete a container, even if
|
||||
it is currently running
|
||||
|
||||
v1.9
|
||||
****
|
||||
|
||||
|
|
|
@ -732,11 +732,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 500: server error
|
||||
|
|
|
@ -742,11 +742,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
1283
docs/sources/reference/api/docker_remote_api_v1.10.rst
Normal file
1283
docs/sources/reference/api/docker_remote_api_v1.10.rst
Normal file
File diff suppressed because it is too large
Load diff
|
@ -761,11 +761,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
|
@ -808,11 +808,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
|
@ -852,11 +852,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
|
@ -831,11 +831,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
|
@ -958,11 +958,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
|
@ -877,11 +877,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
|
@ -118,6 +118,7 @@ Create a container
|
|||
"User":"",
|
||||
"Memory":0,
|
||||
"MemorySwap":0,
|
||||
"CpuShares":0,
|
||||
"AttachStdin":false,
|
||||
"AttachStdout":true,
|
||||
"AttachStderr":true,
|
||||
|
@ -153,7 +154,15 @@ Create a container
|
|||
"Warnings":[]
|
||||
}
|
||||
|
||||
:jsonparam config: the container's configuration
|
||||
:jsonparam Hostname: Container host name
|
||||
:jsonparam User: Username or UID
|
||||
:jsonparam Memory: Memory Limit in bytes
|
||||
:jsonparam CpuShares: CPU shares (relative weight)
|
||||
:jsonparam AttachStdin: 1/True/true or 0/False/false, attach to standard input. Default false
|
||||
:jsonparam AttachStdout: 1/True/true or 0/False/false, attach to standard output. Default false
|
||||
:jsonparam AttachStderr: 1/True/true or 0/False/false, attach to standard error. Default false
|
||||
:jsonparam Tty: 1/True/true or 0/False/false, allocate a pseudo-tty. Default false
|
||||
:jsonparam OpenStdin: 1/True/true or 0/False/false, keep stdin open even if not attached. Default false
|
||||
:query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
|
||||
:statuscode 201: no error
|
||||
:statuscode 404: no such container
|
||||
|
@ -394,7 +403,11 @@ Start a container
|
|||
HTTP/1.1 204 No Content
|
||||
Content-Type: text/plain
|
||||
|
||||
:jsonparam hostConfig: the container's host configuration (optional)
|
||||
:jsonparam Binds: Create a bind mount to a directory or file with [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume.
|
||||
:jsonparam LxcConf: Map of custom lxc options
|
||||
:jsonparam PortBindings: Expose ports from the container, optionally publishing them via the HostPort flag
|
||||
:jsonparam PublishAllPorts: 1/True/true or 0/False/false, publish all exposed ports to the host interfaces. Default false
|
||||
:jsonparam Privileged: 1/True/true or 0/False/false, give extended privileges to this container. Default false
|
||||
:statuscode 204: no error
|
||||
:statuscode 404: no such container
|
||||
:statuscode 500: server error
|
||||
|
@ -892,11 +905,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
|
|
@ -118,6 +118,7 @@ Create a container
|
|||
"User":"",
|
||||
"Memory":0,
|
||||
"MemorySwap":0,
|
||||
"CpuShares":0,
|
||||
"AttachStdin":false,
|
||||
"AttachStdout":true,
|
||||
"AttachStderr":true,
|
||||
|
@ -153,7 +154,15 @@ Create a container
|
|||
"Warnings":[]
|
||||
}
|
||||
|
||||
:jsonparam config: the container's configuration
|
||||
:jsonparam Hostname: Container host name
|
||||
:jsonparam User: Username or UID
|
||||
:jsonparam Memory: Memory Limit in bytes
|
||||
:jsonparam CpuShares: CPU shares (relative weight)
|
||||
:jsonparam AttachStdin: 1/True/true or 0/False/false, attach to standard input. Default false
|
||||
:jsonparam AttachStdout: 1/True/true or 0/False/false, attach to standard output. Default false
|
||||
:jsonparam AttachStderr: 1/True/true or 0/False/false, attach to standard error. Default false
|
||||
:jsonparam Tty: 1/True/true or 0/False/false, allocate a pseudo-tty. Default false
|
||||
:jsonparam OpenStdin: 1/True/true or 0/False/false, keep stdin open even if not attached. Default false
|
||||
:query name: Assign the specified name to the container. Must match ``/?[a-zA-Z0-9_-]+``.
|
||||
:statuscode 201: no error
|
||||
:statuscode 404: no such container
|
||||
|
@ -394,7 +403,11 @@ Start a container
|
|||
HTTP/1.1 204 No Content
|
||||
Content-Type: text/plain
|
||||
|
||||
:jsonparam hostConfig: the container's host configuration (optional)
|
||||
:jsonparam Binds: Create a bind mount to a directory or file with [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume.
|
||||
:jsonparam LxcConf: Map of custom lxc options
|
||||
:jsonparam PortBindings: Expose ports from the container, optionally publishing them via the HostPort flag
|
||||
:jsonparam PublishAllPorts: 1/True/true or 0/False/false, publish all exposed ports to the host interfaces. Default false
|
||||
:jsonparam Privileged: 1/True/true or 0/False/false, give extended privileges to this container. Default false
|
||||
:statuscode 204: no error
|
||||
:statuscode 404: no such container
|
||||
:statuscode 500: server error
|
||||
|
@ -892,11 +905,11 @@ Tag an image into a repository
|
|||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
HTTP/1.1 201 OK
|
||||
|
||||
:query repo: The repository to tag in
|
||||
:query force: 1/True/true or 0/False/false, default false
|
||||
:statuscode 200: no error
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: bad parameter
|
||||
:statuscode 404: no such image
|
||||
:statuscode 409: conflict
|
||||
|
@ -993,12 +1006,12 @@ Search images
|
|||
2.3 Misc
|
||||
--------
|
||||
|
||||
Build an image from Dockerfile via stdin
|
||||
****************************************
|
||||
Build an image from Dockerfile
|
||||
******************************
|
||||
|
||||
.. http:post:: /build
|
||||
|
||||
Build an image from Dockerfile via stdin
|
||||
Build an image from Dockerfile using a POST body.
|
||||
|
||||
**Example request**:
|
||||
|
||||
|
@ -1032,6 +1045,7 @@ Build an image from Dockerfile via stdin
|
|||
:query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
|
||||
:query q: suppress verbose build output
|
||||
:query nocache: do not use the cache when building the image
|
||||
:query rm: Remove intermediate containers after a successful build
|
||||
:reqheader Content-type: should be set to ``"application/tar"``.
|
||||
:reqheader X-Registry-Config: base64-encoded ConfigFile object
|
||||
:statuscode 200: no error
|
||||
|
|
|
@ -15,4 +15,6 @@ Your programs and scripts can access Docker's functionality via these interfaces
|
|||
index_api
|
||||
docker_remote_api
|
||||
remote_api_client_libraries
|
||||
docker_io_oauth_api
|
||||
docker_io_accounts_api
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:title: Remote API Client Libraries
|
||||
:description: Various client libraries available to use with the Docker remote API
|
||||
:keywords: API, Docker, index, registry, REST, documentation, clients, Python, Ruby, Javascript, Erlang, Go
|
||||
:keywords: API, Docker, index, registry, REST, documentation, clients, Python, Ruby, JavaScript, Erlang, Go
|
||||
|
||||
|
||||
==================================
|
||||
|
@ -21,12 +21,18 @@ and we will add the libraries here.
|
|||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| Ruby | docker-api | https://github.com/swipely/docker-api | Active |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| Javascript (NodeJS) | docker.io | https://github.com/appersonlabs/docker.io | Active |
|
||||
| JavaScript (NodeJS) | dockerode | https://github.com/apocas/dockerode | Active |
|
||||
| | | Install via NPM: `npm install dockerode` | |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| JavaScript (NodeJS) | docker.io | https://github.com/appersonlabs/docker.io | Active |
|
||||
| | | Install via NPM: `npm install docker.io` | |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| Javascript | docker-js | https://github.com/dgoujard/docker-js | Active |
|
||||
| JavaScript | docker-js | https://github.com/dgoujard/docker-js | Outdated |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| Javascript (Angular) | dockerui | https://github.com/crosbymichael/dockerui | Active |
|
||||
| JavaScript (Angular) | docker-cp | https://github.com/13W/docker-cp | Active |
|
||||
| **WebUI** | | | |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| JavaScript (Angular) | dockerui | https://github.com/crosbymichael/dockerui | Active |
|
||||
| **WebUI** | | | |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| Java | docker-java | https://github.com/kpelykh/docker-java | Active |
|
||||
|
@ -37,3 +43,5 @@ and we will add the libraries here.
|
|||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| PHP | Alvine | http://pear.alvine.io/ (alpha) | Active |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
| PHP | Docker-PHP | http://stage1.github.io/docker-php/ | Active |
|
||||
+----------------------+----------------+--------------------------------------------+----------+
|
||||
|
|
|
@ -74,7 +74,7 @@ When you're done with your build, you're ready to look into
|
|||
2. Format
|
||||
=========
|
||||
|
||||
The Dockerfile format is quite simple:
|
||||
Here is the format of the Dockerfile:
|
||||
|
||||
::
|
||||
|
||||
|
@ -251,9 +251,14 @@ value ``<value>``. This value will be passed to all future ``RUN``
|
|||
instructions. This is functionally equivalent to prefixing the command
|
||||
with ``<key>=<value>``
|
||||
|
||||
The environment variables set using ``ENV`` will persist when a container is run
|
||||
from the resulting image. You can view the values using ``docker inspect``, and change them using ``docker run --env <key>=<value>``.
|
||||
|
||||
.. note::
|
||||
The environment variables will persist when a container is run
|
||||
from the resulting image.
|
||||
One example where this can cause unexpected consequenses, is setting
|
||||
``ENV DEBIAN_FRONTEND noninteractive``.
|
||||
Which will persist when the container is run interactively; for example:
|
||||
``docker run -t -i image bash``
|
||||
|
||||
.. _dockerfile_add:
|
||||
|
||||
|
@ -461,6 +466,8 @@ For example you might add something like this:
|
|||
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
|
||||
[...]
|
||||
|
||||
.. warning:: Chaining ONBUILD instructions using `ONBUILD ONBUILD` isn't allowed.
|
||||
.. warning:: ONBUILD may not trigger FROM or MAINTAINER instructions.
|
||||
|
||||
.. _dockerfile_examples:
|
||||
|
||||
|
|
|
@ -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. When host=[0.0.0.0], port=[4243] or path=[/var/run/docker.sock] is omitted, default values are used.
|
||||
-H=[unix:///var/run/docker.sock]: tcp://[host]:port to bind/connect to or unix://[/path/to/socket] to use. When host=[127.0.0.1] is omitted for tcp or path=[/var/run/docker.sock] is omitted for unix sockets, default values are used.
|
||||
|
||||
A self-sufficient runtime for linux containers.
|
||||
|
||||
|
@ -20,8 +20,12 @@ To list available commands, either run ``docker`` with no parameters or execute
|
|||
|
||||
.. _cli_options:
|
||||
|
||||
Types of Options
|
||||
----------------
|
||||
Options
|
||||
-------
|
||||
|
||||
Single character commandline options can be combined, so rather than typing
|
||||
``docker run -t -i --name test busybox sh``, you can write
|
||||
``docker run -ti --name test busybox sh``.
|
||||
|
||||
Boolean
|
||||
~~~~~~~
|
||||
|
@ -67,6 +71,7 @@ Commands
|
|||
Usage of docker:
|
||||
-D, --debug=false: Enable debug mode
|
||||
-H, --host=[]: Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise. systemd socket activation can be used with fd://[socketfd].
|
||||
-G, --group="docker": Group to assign the unix socket specified by -H when running in daemon mode; use '' (the empty string) to disable setting of a group
|
||||
--api-enable-cors=false: Enable CORS headers in the remote API
|
||||
-b, --bridge="": Attach containers to a pre-existing network bridge; use 'none' to disable container networking
|
||||
--bip="": Use this CIDR notation address for the network bridge's IP, not compatible with -b
|
||||
|
@ -79,8 +84,9 @@ Commands
|
|||
-p, --pidfile="/var/run/docker.pid": Path to use for daemon PID file
|
||||
-r, --restart=true: Restart previously running containers
|
||||
-s, --storage-driver="": Force the docker runtime to use a specific storage driver
|
||||
-e, --exec-driver="native": Force the docker runtime to use a specific exec driver
|
||||
-v, --version=false: Print version information and quit
|
||||
-mtu, --mtu=0: Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if not default route is available
|
||||
--mtu=0: Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available
|
||||
|
||||
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.
|
||||
|
@ -91,6 +97,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``.
|
||||
|
||||
To use lxc as the execution driver, use ``docker -d -e lxc``.
|
||||
|
||||
The docker client will also honor the ``DOCKER_HOST`` environment variable to set
|
||||
the ``-H`` flag for the client.
|
||||
|
||||
|
@ -102,12 +110,21 @@ the ``-H`` flag for the client.
|
|||
docker ps
|
||||
# both are equal
|
||||
|
||||
|
||||
To run the daemon with `systemd socket activation <http://0pointer.de/blog/projects/socket-activation.html>`_, use ``docker -d -H fd://``.
|
||||
Using ``fd://`` will work perfectly for most setups but you can also specify individual sockets too ``docker -d -H fd://3``.
|
||||
If the specified socket activated files aren't found then docker will exit.
|
||||
You can find examples of using systemd socket activation with docker and systemd in the `docker source tree <https://github.com/dotcloud/docker/blob/master/contrib/init/systemd/socket-activation/>`_.
|
||||
|
||||
Docker supports softlinks for the Docker data directory (``/var/lib/docker``) and for ``/tmp``.
|
||||
TMPDIR and the data directory can be set like this:
|
||||
|
||||
::
|
||||
|
||||
TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
|
||||
# or
|
||||
export TMPDIR=/mnt/disk2/tmp
|
||||
/usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
|
||||
|
||||
.. _cli_attach:
|
||||
|
||||
``attach``
|
||||
|
@ -179,17 +196,18 @@ Examples:
|
|||
|
||||
Usage: docker build [OPTIONS] PATH | URL | -
|
||||
Build a new container image from the source code at PATH
|
||||
-t, --time="": Repository name (and optionally a tag) to be applied
|
||||
-t, --tag="": Repository name (and optionally a tag) to be applied
|
||||
to the resulting image in case of success.
|
||||
-q, --quiet=false: Suppress verbose build output.
|
||||
-q, --quiet=false: Suppress the verbose output generated by the containers.
|
||||
--no-cache: Do not use the cache when building the image.
|
||||
--rm: Remove intermediate containers after a successful build
|
||||
--rm=true: 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 <dockerfile_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
|
||||
``URL``, then the repository is used as the context. Git repositories are
|
||||
cloned with their submodules (`git clone --recursive`).
|
||||
|
||||
.. _cli_build_examples:
|
||||
|
||||
|
@ -223,6 +241,9 @@ Examples:
|
|||
---> Running in 02071fceb21b
|
||||
---> f52f38b7823e
|
||||
Successfully built f52f38b7823e
|
||||
Removing intermediate container 9c9e81692ae9
|
||||
Removing intermediate container 02071fceb21b
|
||||
|
||||
|
||||
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``
|
||||
|
@ -237,6 +258,9 @@ The transfer of context from the local machine to the Docker daemon is
|
|||
what the ``docker`` client means when you see the "Uploading context"
|
||||
message.
|
||||
|
||||
If you wish to keep the intermediate containers after the build is complete,
|
||||
you must use ``--rm=false``. This does not affect the build cache.
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -504,7 +528,7 @@ For example:
|
|||
Show the history of an image
|
||||
|
||||
--no-trunc=false: Don't truncate output
|
||||
-q, --quiet=false: only show numeric IDs
|
||||
-q, --quiet=false: Only show numeric IDs
|
||||
|
||||
To see how the ``docker:latest`` image was built:
|
||||
|
||||
|
@ -551,11 +575,11 @@ To see how the ``docker:latest`` image was built:
|
|||
|
||||
List images
|
||||
|
||||
-a, --all=false: show all images (by default filter out the intermediate images used to build)
|
||||
-a, --all=false: Show all images (by default filter out the intermediate images used to build)
|
||||
--no-trunc=false: Don't truncate output
|
||||
-q, --quiet=false: only show numeric IDs
|
||||
--tree=false: output graph in tree format
|
||||
--viz=false: output graph in graphviz format
|
||||
-q, --quiet=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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -792,6 +816,19 @@ we ask for the ``HostPort`` field to get the public address.
|
|||
|
||||
$ sudo docker inspect -format='{{(index (index .NetworkSettings.Ports "8787/tcp") 0).HostPort}}' $INSTANCE_ID
|
||||
|
||||
Get config
|
||||
..........
|
||||
|
||||
The ``.Field`` syntax doesn't work when the field contains JSON data,
|
||||
but the template language's custom ``json`` function does. The ``.config``
|
||||
section contains complex json object, so to grab it as JSON, you use ``json``
|
||||
to convert config object into JSON
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo docker inspect -format='{{json .config}}' $INSTANCE_ID
|
||||
|
||||
|
||||
.. _cli_kill:
|
||||
|
||||
``kill``
|
||||
|
@ -838,9 +875,9 @@ Known Issues (kill)
|
|||
|
||||
Register or Login to the docker registry server
|
||||
|
||||
-e, --email="": email
|
||||
-p, --password="": password
|
||||
-u, --username="": username
|
||||
-e, --email="": Email
|
||||
-p, --password="": Password
|
||||
-u, --username="": Username
|
||||
|
||||
If you want to login to a private registry you can
|
||||
specify this by adding the server name.
|
||||
|
@ -911,6 +948,8 @@ Running ``docker ps`` showing 2 linked containers.
|
|||
|
||||
The last container is marked as a ``Ghost`` container. It is a container that was running when the docker daemon was restarted (upgraded, or ``-H`` settings changed). The container is still running, but as this docker daemon process is not able to manage it, you can't attach to it. To bring them out of ``Ghost`` Status, you need to use ``docker kill`` or ``docker restart``.
|
||||
|
||||
``docker ps`` will show only running containers by default. To see all containers: ``docker ps -a``
|
||||
|
||||
.. _cli_pull:
|
||||
|
||||
``pull``
|
||||
|
@ -956,7 +995,8 @@ The last container is marked as a ``Ghost`` container. It is a container that wa
|
|||
Usage: docker rm [OPTIONS] CONTAINER
|
||||
|
||||
Remove one or more containers
|
||||
--link="": Remove the link instead of the actual container
|
||||
-l, --link="": Remove the link instead of the actual container
|
||||
-f, --force=false: Force removal of running container
|
||||
|
||||
Known Issues (rm)
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
@ -1005,6 +1045,8 @@ containers will not be deleted.
|
|||
Usage: docker rmi IMAGE [IMAGE...]
|
||||
|
||||
Remove one or more images
|
||||
|
||||
-f, --force=false: Force
|
||||
|
||||
Removing tagged images
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -1054,7 +1096,7 @@ image is removed.
|
|||
--cidfile="": Write the container ID to the file
|
||||
-d, --detach=false: Detached mode: Run container in the background, print new container id
|
||||
-e, --env=[]: Set environment variables
|
||||
-h, --host="": Container host name
|
||||
-h, --hostname="": Container host name
|
||||
-i, --interactive=false: Keep stdin open even if not attached
|
||||
--privileged=false: Give extended privileges to this container
|
||||
-m, --memory="": Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
||||
|
@ -1079,10 +1121,15 @@ 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``.
|
||||
Once the container is stopped it still exists and can be started back up. See ``docker ps -a`` to view a list of all containers.
|
||||
|
||||
The ``docker run`` command can be used in combination with ``docker commit`` to
|
||||
:ref:`change the command that a container runs <cli_commit_examples>`.
|
||||
|
||||
See :ref:`port_redirection` for more detailed information about the ``--expose``,
|
||||
``-p``, ``-P`` and ``--link`` parameters, and :ref:`working_with_links_names` for
|
||||
specific examples using ``--link``.
|
||||
|
||||
Known Issues (run -volumes-from)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -1202,7 +1249,7 @@ 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
|
||||
referenced containers. Containers can be specified by a comma seperated
|
||||
referenced containers. Containers can be specified by a comma separated
|
||||
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
|
||||
read-only or read-write mode, respectively. By default, the volumes are mounted
|
||||
|
@ -1291,7 +1338,7 @@ The main process inside the container will receive SIGTERM, and after a grace pe
|
|||
|
||||
::
|
||||
|
||||
Usage: docker tag [OPTIONS] IMAGE REPOSITORY[:TAG]
|
||||
Usage: docker tag [OPTIONS] IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]
|
||||
|
||||
Tag an image into a repository
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue