Compare commits

...

57 Commits

Author SHA1 Message Date
James Hunt e87d4b5af0 Clear Linux VERSION and default exec driver.
Signed-off-by: James Hunt <james.o.hunt@intel.com>
2016-02-12 13:26:21 +00:00
James Hunt 0a19f03d99 Clear Containers for Docker Engine (v1.9.1)
Signed-off-by: James Hunt <james.o.hunt@intel.com>
2016-02-12 13:26:08 +00:00
Antonio Murdaca 556ea285db Dockerfile: add kvmtool and linux-container for clr execdriver
This patch adds the needed pkgs to docker/docker development Dockerfile
in order to test the clr execdriver in the development container.

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2016-02-12 13:26:01 +00:00
Tibor Vass a34a1d598c Bump version to v1.9.1
Signed-off-by: Tibor Vass <tibor@docker.com>
2015-11-20 07:40:30 -05:00
Madhu Venugopal b6bb8c26fb Fixed an endpoint delete issue with a possible stale sbox
Stale sandbox could be a result of stress tests on ungraceful
restarts such as #17984

Signed-off-by: Madhu Venugopal <madhu@docker.com>
(cherry picked from commit 423b55c4b4aaaa55188b3aeca2b6b2005b19e7ea)
2015-11-20 07:24:48 -05:00
Aaron Lehmann a3004aea15 Correct parent chain in v2 push when v1Compatibility files on the disk are inconsistent
This fixes an issue where two images with the same filesystem contents
and configuration but different remote IDs could share a v1Compatibility
file, resulting in corrupted manifests.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
(cherry picked from commit 0ab6b1d9221f7a2a65c6565fed8f3d6f29fcec2d)

Conflicts:
	integration-cli/docker_cli_push_test.go
2015-11-20 07:24:48 -05:00
Anusha Ragunathan 8d7d152b87 Fix devmapper backend in docker info
Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit fdc2641c2b329c48cf1d1c8c0b806bec8784b3e0)
2015-11-20 07:24:48 -05:00
David Calavera 04c702f617 Make NormalizeLocalName to not reach the network to normalize names.
Signed-off-by: David Calavera <david.calavera@gmail.com>
(cherry picked from commit b665730d2c76cf991c123ad3387d6ba736a7515a)
2015-11-20 07:24:47 -05:00
Harald Albers 842733a582 bash completion for docker {run,create} --volume-driver
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit d411c47139e9a6d94c54bfb4052e7d12bf4295f0)
2015-11-20 07:24:47 -05:00
pidster f1d9a79214 Add Weave Network Plugin to docs/extend/plugins.md
Signed-off-by: pidster <pid@pidster.com>
(cherry picked from commit f57073c72bfb30b083730be0623210ad5381ec58)
2015-11-20 07:24:47 -05:00
Flavio Castelli f97b8f403f Update SUSE/openSUSE installation script
Fix installation on SUSE Linux Enterprise machine, the updated Docker RPMs have
been moved to a different location.

Update both openSUSE and SUSE Linux Enterprise scripts to allow the
installation of experimental builds.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
(cherry picked from commit 400ac8a66c)
2015-11-20 07:24:47 -05:00
Sebastiaan van Stijn b5cc212e75 docs: fix link to "run reference"
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit f2705fa6ede76ad8b307ca184d3e146f5d69f7e6)
2015-11-12 15:13:00 -05:00
Steve Durrheimer 45dbc03636 Add zsh completion for 'docker {run,create} --volume-driver'
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
(cherry picked from commit 8446a5c88bd744df7f7468ec7eb8d8dee4e7c7e9)
2015-11-12 15:13:00 -05:00
Anusha Ragunathan a22e1dd557 Include xfsprogs in build environment.
devmapper uses xfs by default now. So include xfsprogs in build
environment. Also update docs to reflect the new default.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit 2b766a455cc01f3fd51be886c9e61c0b6a5fdeb1)
2015-11-12 15:13:00 -05:00
Vivek Goyal ad0298c5a1 devmapper: Switch to xfs as default filesystem if supported
If platform supports xfs filesystem then use xfs as default filesystem
for container rootfs instead of ext4. Reason being that ext4 is pre-allcating
lot of metadata (around 1.8GB on 100G thin volume) and that can take long
enough on AWS storage that systemd times out and docker fails to start.

If one disables pre-allocation of ext4 metadata, then it will be allocated
when containers are mounted and we will have multiple copies of metadata
per container. For a 100G thin device, it was around 1.5GB of metadata
per container.

ext4 has an optimization to skip zeroing if discards are issued and
underlying device guarantees that zero will be returned when discarded
blocks are read back. devicemapper thin devices don't offer that guarantee
so ext4 optimization does not kick in. In fact given discards are optional
and can be dropped on the floor if need be, it looks like it might not be
possible to guarantee that all the blocks got discarded and if read back
zero will be returned.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
(cherry picked from commit 07ff17fb850e5ddae6f38cc21776ebb9b1690f3e)
2015-11-12 15:13:00 -05:00
Vivek Goyal 039cf28484 devmapper: Warn if user specified a filesytem and base device already has fs
If user wants to use a filesystem it can be specified using dm.fs=<filesystem>
option. It is possible that docker already had base image and a filesystem
on that. Later if user wants to change file system using dm.fs= option
and restarts docker, that's not possible. Warn user about it.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
(cherry picked from commit 83a34e000b2332d9a1b4214a77fae021ed144acb)
2015-11-12 15:13:00 -05:00
Dave Tucker 6702fac9af Add required ports for Overlay to docs
Fixes #17709

Signed-off-by: Dave Tucker <dt@docker.com>
(cherry picked from commit 44a527d877faf0ef62bc9b0221f656041833ae66)
2015-11-12 15:13:00 -05:00
Mary Anthony 94ef56b8b3 Adding in Contiv plugins
Fixes #17764
Also fixed problem where the menu ordering was off
Fix the wrap

Signed-off-by: Mary Anthony <mary@docker.com>
(cherry picked from commit 40fc3a8bcf39e52e45c2aac3808987a5499094d8)
2015-11-12 15:12:59 -05:00
David Currie 5b0ca7c58a Updates to Get started with multi-host networking
Updates primarily to expand usage of Swarm cluster and make Compose
section more verbose plus other minor corrections.

Signed-off-by: David Currie <david_currie@uk.ibm.com>
(cherry picked from commit f16dceb63233bd2c1fd327b92d7112e8012347ac)
2015-11-12 15:12:59 -05:00
Lei Jitang 5d417ee6e8 Test to verify --mac-address doesn't get applied to new endpoint
created on network connect.

Signed-off-by: Lei Jitang <leijitang@huawei.com>
Signed-off-by: Santhosh Manohar <santhosh@docker.com>
(cherry picked from commit 451b79d69102ad95f1b91a52bb4b17c240f41e56)
2015-11-12 15:12:59 -05:00
Santhosh Manohar 9a1d077d9c Allow port mapping only for endpoint created on docker run
Signed-off-by: Santhosh Manohar <santhosh@docker.com>
(cherry picked from commit dedcf6a6ff6d3aa75efaeb31c197b1c3a19305be)
2015-11-12 15:12:59 -05:00
Alessandro Boch a2f92e92b5 Restore IPv6 from MAC on default bridge network
Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit 095a8ac5451de25a91fc569a6633fdd8d4cabb3f)
2015-11-12 15:12:59 -05:00
Alessandro Boch 3950ab5741 Vendoring libnetwork e8ebc0bf6510343c88d162db08b3d855cbbe75b9
Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit 43b3b6ecb4f838023478c484d32957386b7dd666)
2015-11-12 15:12:59 -05:00
Alessandro Boch 200258c2a0 Fix fixed-cidr daemon option
- Logic is broken when gateway ip falls in ip-range
  and ip-range is not the first block in the network

Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit c7cd65962448888ceb1cec6a355fc7fd04f3be53)
2015-11-12 15:12:58 -05:00
Alessandro Boch e0d0582d97 Vendoring libnetwork 5978c276ad20e104d6acd749da6ee6a8930055ae
- To bring in ip-range fix

Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit 9c395537bce1e787ae34d90b2de81df3a47c5ff9)
2015-11-12 15:12:58 -05:00
Harald Albers da9b925066 Fix bash completion on systems where extglob is not set
Signed-off-by: Harald Albers <github@albersweb.de>
(cherry picked from commit 211e3b0df67d17989c7aff63d1a0098ad61f07a3)
2015-11-12 15:12:58 -05:00
Justin Cormack 736ba2937e Use the SKS port 80 keyserver pool, as it is more reliable than the MIT server
Signed-off-by: Justin Cormack <justin@specialbusservice.com>
(cherry picked from commit 4fca870212ea11010e9dc5ecc802d076dcc1c683)
2015-11-12 15:12:58 -05:00
Kai Qiang Wu(Kennan) fe5baaf228 Fixing wrong volume doc format
Signed-off-by: Kai Qiang Wu(Kennan) <wkqwu@cn.ibm.com>
(cherry picked from commit a874ce08f3742d219f105a8b3d5cb7f37544824b)
2015-11-12 15:12:58 -05:00
Christopher Jones 5fd7bc0d74 Fixed broken links in docker_remote_api
Signed-off-by: Christopher Jones <tophj@linux.vnet.ibm.com>
(cherry picked from commit 93939c3f9fda70930ec2d3af4c6231ccfbc6b8ef)
2015-11-12 15:12:58 -05:00
Lei Jitang 2d6c4af15e Prevent connecting to host and prevent disconnecting from host
Container has private network namespace can not to connect to host
and container with host network can not be disconnected from host.

Signed-off-by: Lei Jitang <leijitang@huawei.com>
(cherry picked from commit a2d8c93fc690c2244a3bed32a2bbeb8dae449f90)
2015-11-10 12:50:34 -05:00
Bryan Boreham 3f089980e4 Fix typo in error message
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
(cherry picked from commit 93405dd037ffb54e7e72d9d36be4a16d1d76fa50)
2015-11-10 12:37:39 -05:00
Alexander Morozov 25493c674b Speedup container list
Remove all unneeded disk operations (reload TagStore, umarshal image)
for checking if image still points to same ID. Now slowest part is
queries to sqlite which hopefuly will be removed soon.

Signed-off-by: Alexander Morozov <lk4d4@docker.com>
(cherry picked from commit f256d8ad2d)
2015-11-10 12:37:39 -05:00
Anthony Dahanne fe7768f54d Implicitly use swarm:latest so that master and agent use the same version of swarm
Signed-off-by: Anthony Dahanne <anthony.dahanne@gmail.com>
(cherry picked from commit 4efe0def0ac2a8b6b74a818d23864f62abfaa521)
2015-11-09 19:12:38 -05:00
Tonis Tiigi c7055ebf84 Fix opq whiteouts problems for files with dot prefix
Fixes #17766

Previously, opaque directory whiteouts on non-native
graphdrivers depended on the file order, meaning
files added with the same layer before the whiteout
file `.wh..wh..opq` were also removed.

If that file happened to have subdirs, then calling
chtimes on those dirs after unpack would fail the pull.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit db3070ab1b9e394e45b1db22b8ed8352272d4410)
2015-11-09 19:06:59 -05:00
David Calavera ed95e3d325 Use an empty slice as default value for DNS, DNSSearch and DNSOptions
So we don't print those <no value> in the client and we don't fail
executing inspect templates with API field names.

Make sure those fields are initialized as empty slices when
a container is loaded from disk and their values are nil.

Signed-off-by: David Calavera <david.calavera@gmail.com>
(cherry picked from commit f1a74a89f89affcfbe311e89aa752b3d551e0340)
2015-11-09 19:06:59 -05:00
Dan Walsh 4f23c44a68 Fix relabel for SELinux
With the changes merged into runc/libcontainer, are now causing
SELinux to attempt a relabel always, even if the user did not
request the relabel.

If the user does not specify Z or z on the volume mount we should
not attempt a relabel.

Signed-off-by: Dan Walsh <dwalsh@redhat.com>
(cherry picked from commit d9011b36176d6245bdc25401633c213d73e14ea0)
2015-11-09 18:36:30 -05:00
Alexander Morozov e35ac39418 Update runc to introduce RelabelNeeded function
Signed-off-by: Alexander Morozov <lk4d4@docker.com>
(cherry picked from commit 00bcc924fa64b16484beeeabbd2969572d03d30d)
2015-11-09 18:36:29 -05:00
Tonis Tiigi f4dbb093eb Fix symlink handling in builder ADD/COPY commands
Fixes #17290

Fixes following issues:

- Cache checksums turning off while walking a broken symlink.

- Cache checksums were taken from symlinks while targets were actually copied.

- Copying a symlink pointing to a file to a directory used the basename of the target as a destination basename, instead of basename of the symlink.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 47da59f7ec4ee0f49d47a9b32abb137bb30b2c48)
2015-11-09 18:36:29 -05:00
Nathan LeClaire 6aa14d58ff Revise commands in Compose section of multi-host quickstart
Signed-off-by: Nathan LeClaire <nathan.leclaire@gmail.com>
(cherry picked from commit 6326e6732dd217ba6c53ba80ba917d72ad0d1ce5)
2015-11-09 18:36:29 -05:00
Alexander Morozov f3827116a3 Return nice client-side message for docker logs
Signed-off-by: Alexander Morozov <lk4d4@docker.com>
(cherry picked from commit e37e329074517f48db8468affc62beb595ab090e)
2015-11-09 18:36:29 -05:00
Guilhem Lettron be83aacf80 Fix missing -d flag in docker.fish
Signed-off-by: Guilhem Lettron <guilhem@lettron.fr>
(cherry picked from commit 4a3cd881721ff7986fcdc3544e67b66a259c2ca6)
2015-11-09 18:36:29 -05:00
John Howard a58b402fc8 Windows: Fix docker login
Signed-off-by: John Howard <jhoward@microsoft.com>
(cherry picked from commit 9c765040a5638429384b56a1b77139c6d4049c75)
2015-11-09 18:36:29 -05:00
Anton Polonskiy 56cc398564 fix typo
Signed-off-by: Anton Polonskiy <anton.polonskiy@gmail.com>
(cherry picked from commit 3f331eaa4092ff3a612a275351b82b44c778d9fa)
2015-11-09 18:36:28 -05:00
Zhang Wei 7aa5d784d7 Fix --cluster-store option parser
`--cluster-store` is of form KV-PROVIDER://KV-URL, this commit makes
sure that KV-URL contains no "://"

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
(cherry picked from commit 0ebdac8bee0c587a8a10e3bcdec46e9d8360e2fb)
2015-11-09 18:36:28 -05:00
Sebastiaan van Stijn 5f285e4df4 Docs: add "Linux kernel extra" to manual install
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 8e2b20580b82c3d63d4dbbff1d6bee5640a46a8b)
2015-11-09 18:36:28 -05:00
Brian Goff 67d34cebf7 Reutrn immediately when inspect connection failed
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 72aeb63a47bcdffe15380302ff09ab3f0447e476)
2015-11-09 18:36:28 -05:00
Jessica Frazelle b633af5875 add fedora 23 for all the crazies who already want it
Signed-off-by: Jessica Frazelle <acidburn@docker.com>
(cherry picked from commit 45788bb62d6dcdc5b429b5ba10216d2af0402119)
2015-11-09 18:36:28 -05:00
Antonio Murdaca 2c3d4a7f15 graph: do not error out if images can't be restored
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
(cherry picked from commit eaa4047792d2495276383332c4ad4c573e5e8275)
2015-11-09 18:36:28 -05:00
Mary Anthony 2c20ae1fef Fixing BTRFS > Btrfs
Signed-off-by: Mary Anthony <mary@docker.com>
(cherry picked from commit 90e16ab8f97fc5146aab2e77a0fa6ec2d225ba32)
2015-11-09 18:36:27 -05:00
Jana Radhakrishnan 8a30a35b11 Fix a corner case issue when daemon panics
There is an extreme corner case where when the daemon
panics at the same time as a container is stopping
and cleaning up the sandbox and the sandbox may have been
left with an inconsistent state. This libnetwork vendoring
fixes that case.

Vendoring in libnetwork @ 5305ea570b85d61dd0fd261cd7e1680da1884678

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
(cherry picked from commit 79cffa5ce4a71b29eed66e26c1fb7f153be5c045)
2015-11-09 18:36:27 -05:00
harold cooper 7e485e614c fix documentation inconsistency for volume create
the example command uses `/world` but the description refers to `/src`

Signed-off-by: Harold Cooper <hrldcpr@gmail.com>
(cherry picked from commit f67da614e9963e626d4ec12fd624249947b97d6a)
2015-11-09 18:36:27 -05:00
Alessandro Boch e1f59b5987 Verify Endpoint.Info() before accessing it
- During concurrent operations in multihost environment,
  it is possible that the implementer of `EndpointInfo`
  is nil. It simply means the endpoint is no longer
  available in the datastore.

Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit 54d22cbd9a04a965c935a693bf403d2c87109b5a)
2015-11-09 18:36:27 -05:00
David Calavera 04c79bb941 Make the stats collector to collect network stats.
Currently, we get the network stats each time per subscriber, causing a
high load of cpu when there are several subscribers per container.

This change makes the daemon to collect once and publish N times, where N is the
number of subscribers per container.

Signed-off-by: David Calavera <david.calavera@gmail.com>
(cherry picked from commit 453473d6a964e58f4a61599bd90d62d0088f0cc2)
2015-11-09 18:36:27 -05:00
Stephen J Day 4158c70f58 Avoid panic on write after close in http
By adding a (*WriteFlusher).Close, we limit the Write calls to possibly
deallocated http response buffers to the lifetime of an http request.
Typically, this is seen as a very confusing panic, the cause is usually a
situation where an http.ResponseWriter is held after request completion. We
avoid the panic by disallowing further writes to the response writer after the
request is completed.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
(cherry picked from commit ec2289b2d9ac79fd5e0f69f56f023dfe8ee78bf8)
2015-11-09 18:36:27 -05:00
Alessandro Boch a9450f35cc Fix bug in initializeNetwork()
- On `docker run --net <network id> ...`
  the bug would cause the container to attempt
  to connect to the network two times
- Also made sure endpoint creation rollback will
  be executed on failures in `func (container *Container) connectToNetwork()`

Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit 45e71a79840887b7974387bedba0544015ab4e3b)
2015-11-09 18:36:26 -05:00
David Calavera 08091ba9fb Turn IPC unmount errors into warnings.
And do not try to unmount empty paths.

Because nobody should be woken up in the middle of the night for them.

Signed-off-by: David Calavera <david.calavera@gmail.com>
(cherry picked from commit a54d5932e3a644317c77d59bc5aee562841d5c20)
2015-11-09 18:36:26 -05:00
Brian Goff 19f3bc00ee Force IPC mount to unmount on daemon shutdown/init
Instead of using `MNT_DETACH` to unmount the container's mqueue/shm
mounts, force it... but only on daemon init and shutdown.

This makes sure that these IPC mounts are cleaned up even when the
daemon is killed.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 78bd17e805b7514505455b10f2fd90962505a3ff)
2015-11-09 18:36:26 -05:00
96 changed files with 2802 additions and 400 deletions
+40
View File
@@ -5,6 +5,46 @@ information on the list of deprecated flags and APIs please have a look at
https://docs.docker.com/misc/deprecated/ where target removal dates can also
be found.
## 1.9.1
## Runtime
- Do not prevent daemon from booting if images could not be restored (#17695)
- Force IPC mount to unmount on daemon shutdown/init (#17539)
- Turn IPC unmount errors into warnings (#17554)
- Fix `docker stats` performance regression (#17638)
- Clarify cryptic error message upon `docker logs` if `--log-driver=none` (#17767)
- Fix seldom panics (#17639, #17634, #17703)
- Fix opq whiteouts problems for files with dot prefix (#17819)
- devicemapper: try defaulting to xfs instead of ext4 for performance reasons (#17903, #17918)
- devicemapper: fix displayed fs in docker info (#17974)
- selinux: only relabel if user requested so with the `z` option (#17450, #17834)
- Do not make network calls when normalizing names (#18014)
## Client
- Fix `docker login` on windows (#17738)
- Fix bug with `docker inspect` output when not connected to daemon (#17715)
- Fix `docker inspect -f {{.HostConfig.Dns}} somecontainer` (#17680)
## Builder
- Fix regression with symlink behavior in ADD/COPY (#17710)
## Networking
- Allow passing a network ID as an argument for `--net` (#17558)
- Fix connect to host and prevent disconnect from host for `host` network (#17476)
- Fix `--fixed-cidr` issue when gateway ip falls in ip-range and ip-range is
not the first block in the network (#17853)
- Restore deterministic `IPv6` generation from `MAC` address on default `bridge` network (#17890)
- Allow port-mapping only for endpoints created on docker run (#17858)
- Fixed an endpoint delete issue with a possible stale sbox (#18102)
## Distribution
- Correct parent chain in v2 push when v1Compatibility files on the disk are inconsistent (#18047)
## 1.9.0 (2015-11-03)
## Runtime
+7
View File
@@ -58,9 +58,16 @@ RUN apt-get update && apt-get install -y \
ruby1.9.1-dev \
s3cmd=1.1.0* \
ubuntu-zfs \
xfsprogs \
libzfs-dev \
--no-install-recommends
# clr
RUN echo deb http://download.opensuse.org/repositories/home:/clearlinux:/preview/xUbuntu_15.04/ / > /etc/apt/sources.list.d/clear-containers-docker.list
RUN apt-get --allow-unauthenticated update && apt-get install -y --force-yes \
kvmtool\
linux-container
# Get lvm2 source for compiling statically
RUN git clone -b v2_02_103 https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
+1
View File
@@ -23,6 +23,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
e2fsprogs \
iptables \
procps \
xfsprogs \
xz-utils \
\
aufs-tools \
+1 -1
View File
@@ -1 +1 @@
1.9.0
1.9.1-clear-containers
+15 -8
View File
@@ -61,14 +61,19 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
for _, name := range cmd.Args() {
if *inspectType == "" || *inspectType == "container" {
obj, _, err = readBody(cli.call("GET", "/containers/"+name+"/json?"+v.Encode(), nil, nil))
if err != nil && *inspectType == "container" {
if strings.Contains(err.Error(), "No such") {
fmt.Fprintf(cli.err, "Error: No such container: %s\n", name)
} else {
fmt.Fprintf(cli.err, "%s", err)
if err != nil {
if err == errConnectionFailed {
return err
}
if *inspectType == "container" {
if strings.Contains(err.Error(), "No such") {
fmt.Fprintf(cli.err, "Error: No such container: %s\n", name)
} else {
fmt.Fprintf(cli.err, "%s", err)
}
status = 1
continue
}
status = 1
continue
}
}
@@ -76,6 +81,9 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
isImage = true
if err != nil {
if err == errConnectionFailed {
return err
}
if strings.Contains(err.Error(), "No such") {
if *inspectType == "" {
fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
@@ -88,7 +96,6 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
status = 1
continue
}
}
if tmpl == nil {
+6
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"os"
"runtime"
"strings"
"github.com/docker/docker/api/types"
@@ -33,6 +34,11 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
cmd.ParseFlags(args, true)
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
if runtime.GOOS == "windows" {
cli.in = os.Stdin
}
serverAddress := registry.IndexServer
if len(cmd.Args()) > 0 {
serverAddress = cmd.Arg(0)
+10
View File
@@ -2,6 +2,7 @@ package client
import (
"encoding/json"
"fmt"
"net/url"
"time"
@@ -11,6 +12,11 @@ import (
"github.com/docker/docker/pkg/timeutils"
)
var validDrivers = map[string]bool{
"json-file": true,
"journald": true,
}
// CmdLogs fetches the logs of a given container.
//
// docker logs [OPTIONS] CONTAINER
@@ -36,6 +42,10 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
return err
}
if !validDrivers[c.HostConfig.LogConfig.Type] {
return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got: %s)", c.HostConfig.LogConfig.Type)
}
v := url.Values{}
v.Set("stdout", "1")
v.Set("stderr", "1")
+11 -4
View File
@@ -66,7 +66,9 @@ func (s *router) getContainersStats(ctx context.Context, w http.ResponseWriter,
w.Header().Set("Content-Type", "application/json")
out = w
} else {
out = ioutils.NewWriteFlusher(w)
wf := ioutils.NewWriteFlusher(w)
out = wf
defer wf.Close()
}
var closeNotifier <-chan bool
@@ -122,11 +124,16 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
}
outStream := ioutils.NewWriteFlusher(w)
// write an empty chunk of data (this is to ensure that the
// HTTP Response is sent immediately, even if the container has
// not yet produced any data)
outStream.Write(nil)
w.WriteHeader(http.StatusOK)
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
output := ioutils.NewWriteFlusher(w)
defer output.Close()
logsConfig := &daemon.ContainerLogsConfig{
Follow: httputils.BoolValue(r, "follow"),
@@ -135,7 +142,7 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
Tail: r.Form.Get("tail"),
UseStdout: stdout,
UseStderr: stderr,
OutStream: outStream,
OutStream: output,
Stop: closeNotifier,
}
+4
View File
@@ -105,6 +105,7 @@ func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r
err error
output = ioutils.NewWriteFlusher(w)
)
defer output.Close()
w.Header().Set("Content-Type", "application/json")
@@ -188,6 +189,7 @@ func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *h
name := vars["name"]
output := ioutils.NewWriteFlusher(w)
defer output.Close()
imagePushConfig := &graph.ImagePushConfig{
MetaHeaders: metaHeaders,
AuthConfig: authConfig,
@@ -218,6 +220,7 @@ func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *htt
w.Header().Set("Content-Type", "application/x-tar")
output := ioutils.NewWriteFlusher(w)
defer output.Close()
var names []string
if name, ok := vars["name"]; ok {
names = []string{name}
@@ -297,6 +300,7 @@ func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.R
version := httputils.VersionFromContext(ctx)
output := ioutils.NewWriteFlusher(w)
defer output.Close()
sf := streamformatter.NewJSONStreamFormatter()
errf := func(err error) error {
// Do not write the error in the http output if it's still empty.
+13 -11
View File
@@ -52,16 +52,6 @@ func (s *router) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Req
return httputils.WriteJSON(w, http.StatusOK, info)
}
func buildOutputEncoder(w http.ResponseWriter) *json.Encoder {
w.Header().Set("Content-Type", "application/json")
outStream := ioutils.NewWriteFlusher(w)
// Write an empty chunk of data.
// This is to ensure that the HTTP status code is sent immediately,
// so that it will not block the receiver.
outStream.Write(nil)
return json.NewEncoder(outStream)
}
func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
@@ -87,7 +77,19 @@ func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.R
return err
}
enc := buildOutputEncoder(w)
w.Header().Set("Content-Type", "application/json")
// This is to ensure that the HTTP status code is sent immediately,
// so that it will not block the receiver.
w.WriteHeader(http.StatusOK)
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
output := ioutils.NewWriteFlusher(w)
defer output.Close()
enc := json.NewEncoder(output)
d := s.daemon
es := d.EventsService
current, l := es.Subscribe()
+11 -2
View File
@@ -199,7 +199,11 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
epl := nw.Endpoints()
for _, e := range epl {
sb := e.Info().Sandbox()
ei := e.Info()
if ei == nil {
continue
}
sb := ei.Sandbox()
if sb == nil {
continue
}
@@ -241,7 +245,12 @@ func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource {
}
er.EndpointID = e.ID()
if iface := e.Info().Iface(); iface != nil {
ei := e.Info()
if ei == nil {
return er
}
if iface := ei.Iface(); iface != nil {
if mac := iface.MacAddress(); mac != nil {
er.MacAddress = mac.String()
}
+12 -1
View File
@@ -33,7 +33,8 @@ type Context interface {
Close() error
// Stat returns an entry corresponding to path if any.
// It is recommended to return an error if path was not found.
Stat(path string) (FileInfo, error)
// If path is a symlink it also returns the path to the target file.
Stat(path string) (string, FileInfo, error)
// Open opens path from the context and returns a readable stream of it.
Open(path string) (io.ReadCloser, error)
// Walk walks the tree of the context with the function passed to it.
@@ -64,6 +65,8 @@ type PathFileInfo struct {
os.FileInfo
// FilePath holds the absolute path to the file.
FilePath string
// Name holds the basename for the file.
FileName string
}
// Path returns the absolute path to the file.
@@ -71,6 +74,14 @@ func (fi PathFileInfo) Path() string {
return fi.FilePath
}
// Name returns the basename of the file.
func (fi PathFileInfo) Name() string {
if fi.FileName != "" {
return fi.FileName
}
return fi.FileInfo.Name()
}
// Hashed defines an extra method intended for implementations of os.FileInfo.
type Hashed interface {
// Hash returns the hash of a file.
+7 -6
View File
@@ -366,7 +366,7 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
// Must be a dir or a file
fi, err := b.context.Stat(origPath)
statPath, fi, err := b.context.Stat(origPath)
if err != nil {
return nil, err
}
@@ -383,11 +383,9 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
hfi.SetHash("file:" + hfi.Hash())
return copyInfos, nil
}
// Must be a dir
var subfiles []string
b.context.Walk(origPath, func(path string, info builder.FileInfo, err error) error {
err = b.context.Walk(statPath, func(path string, info builder.FileInfo, err error) error {
if err != nil {
return err
}
@@ -395,6 +393,9 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
subfiles = append(subfiles, info.(builder.Hashed).Hash())
return nil
})
if err != nil {
return nil, err
}
sort.Strings(subfiles)
hasher := sha256.New()
@@ -604,9 +605,9 @@ func (b *Builder) readDockerfile() error {
// back to 'Dockerfile' and use that in the error message.
if b.DockerfileName == "" {
b.DockerfileName = api.DefaultDockerfileName
if _, err := b.context.Stat(b.DockerfileName); os.IsNotExist(err) {
if _, _, err := b.context.Stat(b.DockerfileName); os.IsNotExist(err) {
lowercase := strings.ToLower(b.DockerfileName)
if _, err := b.context.Stat(lowercase); err == nil {
if _, _, err := b.context.Stat(lowercase); err == nil {
b.DockerfileName = lowercase
}
}
+28 -35
View File
@@ -5,7 +5,6 @@ import (
"io"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/chrootarchive"
@@ -43,26 +42,32 @@ func (c *tarSumContext) Open(path string) (io.ReadCloser, error) {
return r, nil
}
func (c *tarSumContext) Stat(path string) (fi FileInfo, err error) {
func (c *tarSumContext) Stat(path string) (string, FileInfo, error) {
cleanpath, fullpath, err := c.normalize(path)
if err != nil {
return nil, err
return "", nil, err
}
st, err := os.Lstat(fullpath)
if err != nil {
return nil, convertPathError(err, cleanpath)
return "", nil, convertPathError(err, cleanpath)
}
fi = PathFileInfo{st, fullpath}
// we set sum to path by default for the case where GetFile returns nil.
// The usual case is if cleanpath is empty.
rel, err := filepath.Rel(c.root, fullpath)
if err != nil {
return "", nil, convertPathError(err, cleanpath)
}
// We set sum to path by default for the case where GetFile returns nil.
// The usual case is if relative path is empty.
sum := path
if tsInfo := c.sums.GetFile(cleanpath); tsInfo != nil {
// Use the checksum of the followed path(not the possible symlink) because
// this is the file that is actually copied.
if tsInfo := c.sums.GetFile(rel); tsInfo != nil {
sum = tsInfo.Sum()
}
fi = &HashedFileInfo{fi, sum}
return fi, nil
fi := &HashedFileInfo{PathFileInfo{st, fullpath, filepath.Base(cleanpath)}, sum}
return rel, fi, nil
}
// MakeTarSumContext returns a build Context from a tar stream.
@@ -114,7 +119,7 @@ func (c *tarSumContext) normalize(path string) (cleanpath, fullpath string, err
if err != nil {
return "", "", fmt.Errorf("Forbidden path outside the build context: %s (%s)", path, fullpath)
}
_, err = os.Stat(fullpath)
_, err = os.Lstat(fullpath)
if err != nil {
return "", "", convertPathError(err, path)
}
@@ -122,38 +127,26 @@ func (c *tarSumContext) normalize(path string) (cleanpath, fullpath string, err
}
func (c *tarSumContext) Walk(root string, walkFn WalkFunc) error {
for _, tsInfo := range c.sums {
path := tsInfo.Name()
path, fullpath, err := c.normalize(path)
root = filepath.Join(c.root, filepath.Join(string(filepath.Separator), root))
return filepath.Walk(root, func(fullpath string, info os.FileInfo, err error) error {
rel, err := filepath.Rel(c.root, fullpath)
if err != nil {
return err
}
// Any file in the context that starts with the given path will be
// picked up and its hashcode used. However, we'll exclude the
// root dir itself. We do this for a coupel of reasons:
// 1 - ADD/COPY will not copy the dir itself, just its children
// so there's no reason to include it in the hash calc
// 2 - the metadata on the dir will change when any child file
// changes. This will lead to a miss in the cache check if that
// child file is in the .dockerignore list.
if rel, err := filepath.Rel(root, path); err != nil {
return err
} else if rel == "." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
continue
if rel == "." {
return nil
}
info, err := os.Lstat(fullpath)
if err != nil {
return convertPathError(err, path)
sum := rel
if tsInfo := c.sums.GetFile(rel); tsInfo != nil {
sum = tsInfo.Sum()
}
// TODO check context breakout?
fi := &HashedFileInfo{PathFileInfo{info, fullpath}, tsInfo.Sum()}
if err := walkFn(path, fi, nil); err != nil {
fi := &HashedFileInfo{PathFileInfo{FileInfo: info, FilePath: fullpath}, sum}
if err := walkFn(rel, fi, nil); err != nil {
return err
}
}
return nil
return nil
})
}
func (c *tarSumContext) Remove(path string) error {
+15
View File
@@ -0,0 +1,15 @@
#
# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"!
#
FROM fedora:23
RUN dnf install -y @development-tools fedora-packager
RUN dnf install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel selinux-policy selinux-policy-devel sqlite-devel tar
ENV GO_VERSION 1.5.1
RUN curl -fSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
ENV PATH $PATH:/usr/local/go/bin
ENV AUTO_GOPATH 1
ENV DOCKER_BUILDTAGS selinux
+11
View File
@@ -42,6 +42,9 @@
# options immediately following their corresponding long form.
# This order should be applied to lists, alternatives and code blocks.
__docker_previous_extglob_setting=$(shopt -p extglob)
shopt -s extglob
__docker_q() {
docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
}
@@ -1367,6 +1370,7 @@ _docker_run() {
--ulimit
--user -u
--uts
--volume-driver
--volumes-from
--volume -v
--workdir -w
@@ -1511,6 +1515,10 @@ _docker_run() {
esac
return
;;
--volume-driver)
COMPREPLY=( $( compgen -W "local" -- "$cur" ) )
return
;;
--volumes-from)
__docker_containers_all
return
@@ -1864,4 +1872,7 @@ _docker() {
return 0
}
eval "$__docker_previous_extglob_setting"
unset __docker_previous_extglob_setting
complete -F _docker docker
+1 -1
View File
@@ -339,7 +339,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l restart -d 'Res
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 security-opt -d 'Security Options'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal 'Signal to kill a container'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal -d 'Signal to kill a container'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)'
+2 -1
View File
@@ -334,7 +334,7 @@ __docker_volume_subcommand() {
(create)
_arguments \
$opts_help \
"($help -d --driver)"{-d,--driver=}"[Specify volume driver name]:Driver name: " \
"($help -d --driver)"{-d,--driver=}"[Specify volume driver name]:Driver name:(local)" \
"($help)--name=[Specify volume name]" \
"($help)*"{-o,--opt=}"[Set driver specific options]:Driver option: " && ret=0
;;
@@ -444,6 +444,7 @@ __docker_subcommand() {
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]"
"($help -u --user)"{-u,--user=}"[Username or UID]:user:_users"
"($help)*-v[Bind mount a volume]:volume: "
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)"
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: "
"($help -w --workdir)"{-w,--workdir=}"[Working directory inside the container]:directory:_directories"
)
+1 -1
View File
@@ -13,7 +13,7 @@ import (
var (
defaultPidFile = "/var/run/docker.pid"
defaultGraph = "/var/lib/docker"
defaultExec = "native"
defaultExec = "clr"
)
// Config defines the configuration of a docker daemon.
+23 -4
View File
@@ -153,7 +153,28 @@ func (container *Container) readHostConfig() error {
}
defer f.Close()
return json.NewDecoder(f).Decode(&container.hostConfig)
if err := json.NewDecoder(f).Decode(&container.hostConfig); err != nil {
return err
}
// Make sure the dns fields are never nil.
// New containers don't ever have those fields nil,
// but pre created containers can still have those nil values.
// See https://github.com/docker/docker/pull/17779
// for a more detailed explanation on why we don't want that.
if container.hostConfig.DNS == nil {
container.hostConfig.DNS = make([]string, 0)
}
if container.hostConfig.DNSSearch == nil {
container.hostConfig.DNSSearch = make([]string, 0)
}
if container.hostConfig.DNSOptions == nil {
container.hostConfig.DNSOptions = make([]string, 0)
}
return nil
}
func (container *Container) writeHostConfig() error {
@@ -333,9 +354,7 @@ func (streamConfig *streamConfig) StderrPipe() io.ReadCloser {
func (container *Container) cleanup() {
container.releaseNetwork()
if err := container.unmountIpcMounts(); err != nil {
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
}
container.unmountIpcMounts(detachMounted)
if err := container.Unmount(); err != nil {
logrus.Errorf("%s: Failed to umount filesystem: %v", container.ID, err)
+70
View File
@@ -1,10 +1,15 @@
package daemon
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers"
)
func TestGetFullName(t *testing.T) {
@@ -64,3 +69,68 @@ func TestContainerStopSignal(t *testing.T) {
t.Fatalf("Expected 9, got %v", s)
}
}
func TestContainerInitDNS(t *testing.T) {
tmp, err := ioutil.TempDir("", "docker-container-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
containerID := "d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e"
containerPath := filepath.Join(tmp, containerID)
if err := os.MkdirAll(containerPath, 0755); err != nil {
t.Fatal(err)
}
config := `{"State":{"Running":true,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":2464,"ExitCode":0,
"Error":"","StartedAt":"2015-05-26T16:48:53.869308965Z","FinishedAt":"0001-01-01T00:00:00Z"},
"ID":"d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e","Created":"2015-05-26T16:48:53.7987917Z","Path":"top",
"Args":[],"Config":{"Hostname":"d59df5276e7b","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"Cpuset":"",
"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":true,"OpenStdin":true,
"StdinOnce":false,"Env":null,"Cmd":["top"],"Image":"ubuntu:latest","Volumes":null,"WorkingDir":"","Entrypoint":null,
"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":{}},"Image":"07f8e8c5e66084bef8f848877857537ffe1c47edd01a93af27e7161672ad0e95",
"NetworkSettings":{"IPAddress":"172.17.0.1","IPPrefixLen":16,"MacAddress":"02:42:ac:11:00:01","LinkLocalIPv6Address":"fe80::42:acff:fe11:1",
"LinkLocalIPv6PrefixLen":64,"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"Gateway":"172.17.42.1","IPv6Gateway":"","Bridge":"docker0","Ports":{}},
"ResolvConfPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/resolv.conf",
"HostnamePath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hostname",
"HostsPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/hosts",
"LogPath":"/var/lib/docker/containers/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e/d59df5276e7b219d510fe70565e0404bc06350e0d4b43fe961f22f339980170e-json.log",
"Name":"/ubuntu","Driver":"aufs","MountLabel":"","ProcessLabel":"","AppArmorProfile":"","RestartCount":0,
"UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}`
if err = ioutil.WriteFile(filepath.Join(containerPath, "config.json"), []byte(config), 0644); err != nil {
t.Fatal(err)
}
hostConfig := `{"Binds":[],"ContainerIDFile":"","Memory":0,"MemorySwap":0,"CpuShares":0,"CpusetCpus":"",
"Privileged":false,"PortBindings":{},"Links":null,"PublishAllPorts":false,"Dns":null,"DnsOptions":null,"DnsSearch":null,"ExtraHosts":null,"VolumesFrom":null,
"Devices":[],"NetworkMode":"bridge","IpcMode":"","PidMode":"","CapAdd":null,"CapDrop":null,"RestartPolicy":{"Name":"no","MaximumRetryCount":0},
"SecurityOpt":null,"ReadonlyRootfs":false,"Ulimits":null,"LogConfig":{"Type":"","Config":null},"CgroupParent":""}`
if err = ioutil.WriteFile(filepath.Join(containerPath, "hostconfig.json"), []byte(hostConfig), 0644); err != nil {
t.Fatal(err)
}
daemon, err := initDaemonWithVolumeStore(tmp)
if err != nil {
t.Fatal(err)
}
defer volumedrivers.Unregister(volume.DefaultDriverName)
c, err := daemon.load(containerID)
if err != nil {
t.Fatal(err)
}
if c.hostConfig.DNS == nil {
t.Fatal("Expected container DNS to not be nil")
}
if c.hostConfig.DNSSearch == nil {
t.Fatal("Expected container DNSSearch to not be nil")
}
if c.hostConfig.DNSOptions == nil {
t.Fatal("Expected container DNSOptions to not be nil")
}
}
+51 -29
View File
@@ -229,10 +229,10 @@ func populateCommand(c *Container, env []string) error {
ipc.HostIpc = c.hostConfig.IpcMode.IsHost()
if ipc.HostIpc {
if _, err := os.Stat("/dev/shm"); err != nil {
return fmt.Errorf("/dev/shm is not mounted, but must be for --host=ipc")
return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host")
}
if _, err := os.Stat("/dev/mqueue"); err != nil {
return fmt.Errorf("/dev/mqueue is not mounted, but must be for --host=ipc")
return fmt.Errorf("/dev/mqueue is not mounted, but must be for --ipc=host")
}
c.ShmPath = "/dev/shm"
c.MqueuePath = "/dev/mqueue"
@@ -621,7 +621,9 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett
return networkSettings, nil
}
networkSettings.Ports = nat.PortMap{}
if networkSettings.Ports == nil {
networkSettings.Ports = nat.PortMap{}
}
if expData, ok := driverInfo[netlabel.ExposedPorts]; ok {
if exposedPorts, ok := expData.([]types.TransportPort); ok {
@@ -723,6 +725,10 @@ func (container *Container) updateNetworkSettings(n libnetwork.Network) error {
container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)}
}
if !container.hostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() {
return runconfig.ErrConflictHostNetwork
}
for s := range container.NetworkSettings.Networks {
sn, err := container.daemon.FindNetwork(s)
if err != nil {
@@ -816,6 +822,17 @@ func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([]
createOptions []libnetwork.EndpointOption
)
if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint {
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
}
// Other configs are applicable only for the endpoint in the network
// to which container was connected to on docker run.
if n.Name() != container.hostConfig.NetworkMode.NetworkName() &&
!(n.Name() == "bridge" && container.hostConfig.NetworkMode.IsDefault()) {
return createOptions, nil
}
if container.Config.ExposedPorts != nil {
portSpecs = container.Config.ExposedPorts
}
@@ -885,10 +902,6 @@ func (container *Container) buildCreateEndpointOptions(n libnetwork.Network) ([]
createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption))
}
if n.Name() == "bridge" || container.NetworkSettings.IsAnonymousEndpoint {
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
}
return createOptions, nil
}
@@ -927,6 +940,13 @@ func (container *Container) allocateNetwork() error {
if mode.IsDefault() {
networkName = controller.Config().Daemon.DefaultNetwork
}
if mode.IsUserDefined() {
n, err := container.daemon.FindNetwork(networkName)
if err != nil {
return err
}
networkName = n.Name()
}
container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings)
container.NetworkSettings.Networks[networkName] = new(network.EndpointSettings)
updateSettings = true
@@ -953,7 +973,7 @@ func (container *Container) getNetworkSandbox() libnetwork.Sandbox {
return sb
}
// ConnectToNetwork connects a container to a netork
// ConnectToNetwork connects a container to a network
func (container *Container) ConnectToNetwork(idOrName string) error {
if !container.Running {
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
@@ -967,9 +987,7 @@ func (container *Container) ConnectToNetwork(idOrName string) error {
return nil
}
func (container *Container) connectToNetwork(idOrName string, updateSettings bool) error {
var err error
func (container *Container) connectToNetwork(idOrName string, updateSettings bool) (err error) {
if container.hostConfig.NetworkMode.IsContainer() {
return runconfig.ErrConflictSharedNetwork
}
@@ -1196,6 +1214,10 @@ func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error {
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
}
if container.hostConfig.NetworkMode.IsHost() && runconfig.NetworkMode(n.Type()).IsHost() {
return runconfig.ErrConflictHostNetwork
}
if err := container.disconnectFromNetwork(n); err != nil {
return err
}
@@ -1213,7 +1235,11 @@ func (container *Container) disconnectFromNetwork(n libnetwork.Network) error {
)
s := func(current libnetwork.Endpoint) bool {
if sb := current.Info().Sandbox(); sb != nil {
epInfo := current.Info()
if epInfo == nil {
return false
}
if sb := epInfo.Sandbox(); sb != nil {
if sb.ContainerID() == container.ID {
ep = current
sbox = sb
@@ -1456,22 +1482,21 @@ func (container *Container) setupIpcDirs() error {
return nil
}
func (container *Container) unmountIpcMounts() error {
func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
if container.hostConfig.IpcMode.IsContainer() || container.hostConfig.IpcMode.IsHost() {
return nil
return
}
var errors []string
var warnings []string
if !container.hasMountFor("/dev/shm") {
shmPath, err := container.shmPath()
if err != nil {
logrus.Error(err)
errors = append(errors, err.Error())
} else {
if err := detachMounted(shmPath); err != nil {
logrus.Errorf("failed to umount %s: %v", shmPath, err)
errors = append(errors, err.Error())
warnings = append(warnings, err.Error())
} else if shmPath != "" {
if err := unmount(shmPath); err != nil {
warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err))
}
}
@@ -1481,20 +1506,17 @@ func (container *Container) unmountIpcMounts() error {
mqueuePath, err := container.mqueuePath()
if err != nil {
logrus.Error(err)
errors = append(errors, err.Error())
} else {
if err := detachMounted(mqueuePath); err != nil {
logrus.Errorf("failed to umount %s: %v", mqueuePath, err)
errors = append(errors, err.Error())
warnings = append(warnings, err.Error())
} else if mqueuePath != "" {
if err := unmount(mqueuePath); err != nil {
warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", mqueuePath, err))
}
}
}
if len(errors) > 0 {
return fmt.Errorf("failed to cleanup ipc mounts:\n%v", strings.Join(errors, "\n"))
if len(warnings) > 0 {
logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n"))
}
return nil
}
func (container *Container) ipcMounts() []execdriver.Mount {
+5 -2
View File
@@ -183,11 +183,14 @@ func (container *Container) removeMountPoints(_ bool) error {
return nil
}
func (container *Container) setupIpcDirs() error {
func (container *Container) unmountIpcMounts(unmount func(pth string) error) {
}
func detachMounted(path string) error {
return nil
}
func (container *Container) unmountIpcMounts() error {
func (container *Container) setupIpcDirs() error {
return nil
}
+3 -3
View File
@@ -38,6 +38,7 @@ import (
"github.com/docker/docker/pkg/graphdb"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/docker/docker/pkg/nat"
"github.com/docker/docker/pkg/parsers/filters"
@@ -222,9 +223,8 @@ func (daemon *Daemon) Register(container *Container) error {
}
daemon.execDriver.Terminate(cmd)
if err := container.unmountIpcMounts(); err != nil {
logrus.Errorf("%s: Failed to umount ipc filesystems: %v", container.ID, err)
}
container.unmountIpcMounts(mount.Unmount)
if err := container.Unmount(); err != nil {
logrus.Debugf("unmount error %s", err)
}
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/mount"
)
// cleanupMounts umounts shm/mqueue mounts for old containers
@@ -20,7 +21,7 @@ func (daemon *Daemon) cleanupMounts() error {
}
defer f.Close()
return daemon.cleanupMountsFromReader(f, detachMounted)
return daemon.cleanupMountsFromReader(f, mount.Unmount)
}
func (daemon *Daemon) cleanupMountsFromReader(reader io.Reader, unmount func(target string) error) error {
+30 -5
View File
@@ -182,7 +182,7 @@ func TestLoadWithVolume(t *testing.T) {
t.Fatal(err)
}
daemon, err := initDaemonForVolumesTest(tmp)
daemon, err := initDaemonWithVolumeStore(tmp)
if err != nil {
t.Fatal(err)
}
@@ -270,7 +270,7 @@ func TestLoadWithBindMount(t *testing.T) {
t.Fatal(err)
}
daemon, err := initDaemonForVolumesTest(tmp)
daemon, err := initDaemonWithVolumeStore(tmp)
if err != nil {
t.Fatal(err)
}
@@ -361,7 +361,7 @@ func TestLoadWithVolume17RC(t *testing.T) {
t.Fatal(err)
}
daemon, err := initDaemonForVolumesTest(tmp)
daemon, err := initDaemonWithVolumeStore(tmp)
if err != nil {
t.Fatal(err)
}
@@ -466,7 +466,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
t.Fatal(err)
}
daemon, err := initDaemonForVolumesTest(tmp)
daemon, err := initDaemonWithVolumeStore(tmp)
if err != nil {
t.Fatal(err)
}
@@ -502,7 +502,7 @@ func TestRemoveLocalVolumesFollowingSymlinks(t *testing.T) {
}
}
func initDaemonForVolumesTest(tmp string) (*Daemon, error) {
func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
daemon := &Daemon{
repository: tmp,
root: tmp,
@@ -549,3 +549,28 @@ func TestParseSecurityOpt(t *testing.T) {
t.Fatal("Expected parseSecurityOpt error, got nil")
}
}
func TestNetworkOptions(t *testing.T) {
daemon := &Daemon{}
dconfigCorrect := &Config{
CommonConfig: CommonConfig{
DefaultNetwork: "netPlugin:mynet:dev",
ClusterStore: "consul://localhost:8500",
ClusterAdvertise: "192.168.0.1:8000",
},
}
if _, err := daemon.networkOptions(dconfigCorrect); err != nil {
t.Fatalf("Expect networkOptions sucess, got error: %v", err)
}
dconfigWrong := &Config{
CommonConfig: CommonConfig{
ClusterStore: "consul://localhost:8500://test://bbb",
},
}
if _, err := daemon.networkOptions(dconfigWrong); err == nil {
t.Fatalf("Expected networkOptions error, got nil")
}
}
+18 -4
View File
@@ -332,11 +332,11 @@ func (daemon *Daemon) networkOptions(dconfig *Config) ([]nwconfig.Option, error)
if strings.TrimSpace(dconfig.ClusterStore) != "" {
kv := strings.Split(dconfig.ClusterStore, "://")
if len(kv) < 2 {
if len(kv) != 2 {
return nil, fmt.Errorf("kv store daemon config must be of the form KV-PROVIDER://KV-URL")
}
options = append(options, nwconfig.OptionKVProvider(kv[0]))
options = append(options, nwconfig.OptionKVProviderURL(strings.Join(kv[1:], "://")))
options = append(options, nwconfig.OptionKVProviderURL(kv[1]))
}
if len(dconfig.ClusterOpts) > 0 {
options = append(options, nwconfig.OptionKVOpts(dconfig.ClusterOpts))
@@ -458,12 +458,25 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String()
}
var ipamV6Conf *libnetwork.IpamConf
var (
ipamV6Conf *libnetwork.IpamConf
deferIPv6Alloc bool
)
if config.Bridge.FixedCIDRv6 != "" {
_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
if err != nil {
return err
}
// In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
// at least 48 host bits, we need to guarantee the current behavior where the containers'
// IPv6 addresses will be constructed based on the containers' interface MAC address.
// We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
// on this network until after the driver has created the endpoint and returned the
// constructed address. Libnetwork will then reserve this address with the ipam driver.
ones, _ := fCIDRv6.Mask.Size()
deferIPv6Alloc = ones <= 80
if ipamV6Conf == nil {
ipamV6Conf = &libnetwork.IpamConf{}
}
@@ -488,7 +501,8 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
netlabel.GenericData: netOption,
netlabel.EnableIPv6: config.Bridge.EnableIPv6,
}),
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf))
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf),
libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
if err != nil {
return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
}
+1 -1
View File
@@ -177,7 +177,7 @@ func (d Docker) Copy(c *daemon.Container, destPath string, src builder.FileInfo,
// only needed for fixPermissions, but might as well put it before CopyFileWithTar
if destExists && destStat.IsDir() {
destPath = filepath.Join(destPath, filepath.Base(srcPath))
destPath = filepath.Join(destPath, src.Name())
}
if err := system.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
+964
View File
@@ -0,0 +1,964 @@
// +build linux
package clr
import (
"crypto/sha512"
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"net/http"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/mount"
sysinfo "github.com/docker/docker/pkg/system"
"github.com/docker/docker/pkg/term"
"github.com/docker/libnetwork/netlabel"
"github.com/kr/pty"
"github.com/opencontainers/runc/libcontainer/configs"
)
const (
// Clear Linux for Intel(R) Architecture
driverName = "clr"
envVarPrefix = "CLR_"
// variable that allows clrURL to be modified
clrEnvURL = envVarPrefix + "DOWNLOAD_URL"
// Command used for lkvm control
lkvmName = "lkvm"
// default value for clrURL
defaultClrURL = "https://download.clearlinux.org"
// upstream latest release file (found below clrURL)
latestFile = "latest"
// local "latest" information
clrFile = "latest"
// ASCII file containing a checksums for the downloaded image.
clrChecksumFile = "SHA512SUMS"
// clr kernel (not bzimage)
clrKernel = "/usr/lib/kernel/vmlinux.container"
)
var (
// upstream base URL
clrURL = defaultClrURL
)
type driver struct {
root string // root path for the driver to use
libPath string
initPath string
version string
apparmor bool
sharedRoot bool
activeContainers map[string]*activeContainer
machineMemory int64
containerPid int
sync.Mutex
ipaddr string
gateway string
macaddr string
}
type activeContainer struct {
container *configs.Config
cmd *exec.Cmd
}
type ProgressReader struct {
// The real reader
io.Reader
// Total size of download
total uint64
// Bytes received
bytes uint64
// Rate-limiting counter
lastShown int
rawURL string
}
// Read implements the Reader interface by calling the _real_ reader
// and periodically displaying progress information.
func (r *ProgressReader) Read(p []byte) (n int, err error) {
n, err = r.Reader.Read(p)
if err != nil && err != io.EOF {
// A true error occured
return n, err
}
r.bytes += uint64(n)
if r.total == 0 {
// http header did not provide size
logrus.Debugf("Read %v bytes from %v", r.bytes, r.rawURL)
} else {
percent := (float64(r.bytes) / float64(r.total)) * 100
// rate-limit to only display messages for every 10% downloaded
next := int(percent / 10)
if r.lastShown == -1 || next > r.lastShown {
logrus.Debugf("Read %v of %v bytes from %v (%2.2f%%)", r.bytes, r.total, r.rawURL, percent)
r.lastShown = next
}
}
return n, err
}
func getTapIf(c *execdriver.Command) string {
return fmt.Sprintf("tb-%s", c.ID[:12])
}
// getClrVersion reads the latest image version from the
// locally-downloaded "latest" file.
func getClrVersion(libPath string) string {
txt, err := ioutil.ReadFile(path.Join(libPath, clrFile))
if err != nil {
return ""
}
return strings.Split(string(txt), "\n")[0]
}
func getURL(rawURL, outfile string) error {
_url, err := url.Parse(rawURL)
if err != nil {
return err
}
out, err := os.Create(outfile)
if err != nil {
return err
}
defer out.Close()
transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
client := &http.Client{Transport: transport}
// Determine size of download by consulting the headers
response, err := client.Head(rawURL)
if err != nil {
return err
}
lenStr := response.Header.Get("Content-Length")
dataLen, _ := strconv.Atoi(lenStr)
request, err := http.NewRequest("GET", _url.String(), nil)
if err != nil {
return err
}
response, err = client.Do(request)
defer response.Body.Close()
reader := &ProgressReader{
Reader: response.Body,
total: uint64(dataLen),
lastShown: -1,
rawURL: rawURL,
}
_, err = io.Copy(out, reader)
return err
}
// fetchLatest downloads the "latest" file which contains the version
// number of the latest image.
func fetchLatest(libPath string) error {
file := path.Join(libPath, clrFile)
return getURL(clrURL+"/"+latestFile, file)
}
// NewDriver creates a new clear linux execution driver.
func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
if err := os.MkdirAll(root, 0700); err != nil {
return nil, err
}
meminfo, err := sysinfo.ReadMemInfo()
if err != nil {
return nil, err
}
version, err := prepareClr(libPath)
if err != nil {
return nil, err
}
return &driver{
apparmor: apparmor,
root: root,
libPath: libPath,
initPath: initPath,
version: version,
sharedRoot: false,
activeContainers: make(map[string]*activeContainer),
// FIXME:
machineMemory: meminfo.MemTotal,
}, nil
}
// fetchImage downloads the latest image and verifies its checksum.
func fetchImage(version, outfile, outpath string) error {
url := fmt.Sprintf("%s/releases/%s/clear/%s", clrURL, version, outfile)
logrus.Debugf("Fetching clr version: %s, %s", version, outpath)
return getURL(url, outpath)
}
// uncompressImage handles decompressing the image specified by path.
func uncompressImage(path string) error {
// decompress the file
cmd := exec.Command("unxz", path)
cmd.Dir = filepath.Dir(path)
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("Unable to extract image %s: %s", path, output)
}
return nil
}
// getChecksumLine reads the file specified and returns the checksum for
// the filename specified.
func getChecksumLine(file string, filename string) string {
data, err := ioutil.ReadFile(file)
if err != nil {
return ""
}
lines := strings.Split(string(data), "\n")
for _, line := range lines {
if strings.HasSuffix(line, filename) {
fields := strings.Fields(line)
if len(fields) == 2 {
return fields[0]
}
}
}
return ""
}
// getChecksum downloads the checksum file for the image version specified,
// storing it under libPath, then extracts the checksum corresponding to
// imageFile and returns it.
func getChecksum(version string, libPath string, imageFile string) (string, error) {
outfile := path.Join(libPath, clrChecksumFile)
checkurl := fmt.Sprintf("%s/releases/%s/clear/%s", clrURL, version, clrChecksumFile)
err := getURL(checkurl, outfile)
if err != nil {
return "", err
}
// Now, extract the checksum from the downloaded file.
return getChecksumLine(outfile, imageFile), nil
}
func verifyChecksum(fullPath, expectedChecksum string) error {
if expectedChecksum == "" {
return errors.New("blank expectedChecksum")
}
if fullPath == "" {
return errors.New("blank path")
}
data, err := ioutil.ReadFile(fullPath)
if err != nil {
return err
}
calculatedChecksum := fmt.Sprintf("%x", sha512.Sum512(data))
if calculatedChecksum != expectedChecksum {
return fmt.Errorf("Checksum mismatch: %v %v", calculatedChecksum, expectedChecksum)
}
logrus.Debugf("Checksum for %v correct: %v", fullPath, expectedChecksum)
return nil
}
// alreadyDownloaded determines if the uncompressed version of the file
// specified by outpath already exists and seems sane.
func alreadyDownloaded(outpath string) bool {
// strip off the (first) extension
base := path.Base(outpath)
extension := filepath.Ext(base)
uncompressed := strings.TrimSuffix(outpath, extension)
st, err := os.Stat(uncompressed)
if err != nil {
return false
}
if st.IsDir() {
return false
}
if st.Size() == 0 {
return false
}
return true
}
// prepareClr handles downloading and checking clr images, returning the
// version of clr that will be used.
func prepareClr(libPath string) (string, error) {
logrus.Debugf("%s preparing environment", driverName)
tmp := os.Getenv(clrEnvURL)
if tmp != "" {
clrURL = tmp
logrus.Debugf("%s using alternate download URL: %s", driverName, clrURL)
}
var version = getClrVersion(libPath)
err := fetchLatest(libPath)
if err != nil {
return "", err
}
nversion := getClrVersion(libPath)
if nversion == "" {
return "", errors.New("unable to determine latest clr version")
}
outfile := fmt.Sprintf("clear-%s-containers.img.xz", nversion)
outpath := path.Join(libPath, outfile)
// If the download originally failed, there may be a partial .xz
// file. However, this will be ignored and a new download triggered
// since we only only check for the uncompressed file (which would
// only exist if the download was successful).
if alreadyDownloaded(outpath) && nversion == version {
logrus.Debugf("Using clr version: %s", nversion)
return nversion, nil
}
// Determine the expected checksum for the file about to be
// downloaded.
checksum, err := getChecksum(nversion, libPath, outfile)
if err != nil {
return "", err
}
if nversion != version && version != "" {
logrus.Debugf("Updating to clr version: %s", nversion)
} else if version == "" {
logrus.Debugf("Installing clr version: %s", nversion)
}
err = fetchImage(nversion, outfile, outpath)
if err != nil {
return "", err
}
if err = verifyChecksum(outpath, checksum); err != nil {
return "", err
}
err = uncompressImage(outpath)
if err != nil {
return "", err
}
return nversion, nil
}
func (d *driver) Name() string {
return fmt.Sprintf("%s-%s", driverName, d.version)
}
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
var (
term execdriver.Terminal
err error
)
container, err := d.createContainer(c)
if err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
memoryMiB := c.HostConfig.Memory
if memoryMiB == 0 {
memoryMiB = 1024
} else {
// docker passes the value as bytes
memoryMiB = memoryMiB / int64(math.Pow(2, 20))
}
workingDirVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "WORKINGDIR", c.WorkingDir)
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, workingDirVar)
userVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "USER", c.ProcessConfig.User)
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, userVar)
if err := d.setupNetwork(c); err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
if c.ProcessConfig.Tty {
term, err = NewTtyConsole(&c.ProcessConfig, pipes)
} else {
term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes)
}
if err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
c.ProcessConfig.Terminal = term
d.Lock()
d.activeContainers[c.ID] = &activeContainer{
container: container,
cmd: &c.ProcessConfig.Cmd,
}
d.Unlock()
if err := d.generateEnvConfig(c); err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
if err := d.generateDockerInit(c); err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
for _, m := range c.Mounts {
dest := path.Join(c.Rootfs, m.Destination)
if m.Destination == "/etc/hostname" {
continue
}
if !pathExists(m.Source) {
continue
}
opts := "bind"
if m.Private {
opts = opts + ",rprivate"
}
if m.Slave {
opts = opts + ",rslave"
}
// This may look racy, but it isn't since the VM isn't
// running yet.
//
// The check is necessary to handle bind mounting of
// regular files correctly since without it we may be
// attempting to create a directory where there already
// exists a normal file.
if !pathExists(dest) {
if err := os.MkdirAll(dest, 0750); err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
}
if err := mount.Mount(m.Source, dest, "", opts); err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
if !m.Writable {
if err := mount.Mount("", dest, "", "bind,remount,ro"); err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
}
defer mount.Unmount(dest)
}
var args []string
// various things for lkvm
ifname := getTapIf(c)
// FIXME: Should be real hostname from like process/container struct
hostname := c.ID[0:12]
img := fmt.Sprintf("%s/clear-%s-containers.img", d.libPath, d.version)
memory := fmt.Sprintf("%d", memoryMiB)
// FIXME: Locked cores to 6 ?
cores := fmt.Sprintf("%d", 6)
args = append(args, c.ProcessConfig.Entrypoint)
args = append(args, c.ProcessConfig.Arguments...)
rootParams := fmt.Sprintf("root=/dev/plkvm0p1 rootfstype=ext4 rootflags=dax,data=ordered "+
"init=/usr/lib/systemd/systemd systemd.unit=container.target rw tsc=reliable "+
"systemd.show_status=false "+
"no_timer_check rcupdate.rcu_expedited=1 console=hvc0 quiet ip=%s::%s::%s::off",
d.ipaddr, d.gateway, hostname)
params := []string{
lkvmName, "run", "-c", cores, "-m", memory,
"--name", c.ID, "--console", "virtio",
"--kernel", clrKernel,
"--params", rootParams,
"--shmem", fmt.Sprintf("0x200000000:0:file=%s:private", img),
"--network", fmt.Sprintf("mode=tap,script=none,tapif=%s,guest_mac=%s", ifname, d.macaddr),
"--9p", fmt.Sprintf("%s,rootfs", c.Rootfs),
}
logrus.Debugf("%s params %s", driverName, params)
var (
name = params[0]
arg = params[1:]
)
aname, err := exec.LookPath(name)
if err != nil {
aname = name
}
c.ProcessConfig.Path = aname
c.ProcessConfig.Args = append([]string{name}, arg...)
c.ProcessConfig.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
// Start the container. Since it runs synchronously, we don't Wait()
// for it since we need to check the status to determine if it did
// actually start successfully.
if err := c.ProcessConfig.Start(); err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
var (
waitErr error
waitLock = make(chan struct{})
)
go func() {
if err := c.ProcessConfig.Wait(); err != nil {
if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
waitErr = err
}
}
close(waitLock)
}()
// FIXME: need to create state.json for Stats() to work.
pid := c.ProcessConfig.Process.Pid
c.ContainerPid = pid
d.containerPid = pid
if hooks.Start != nil {
logrus.Debugf("Invoking startCallback")
chOOM := make(chan struct{})
close(chOOM)
hooks.Start(&c.ProcessConfig, pid, chOOM)
}
// FIXME:
oomKill := false
// Wait for the VM to shutdown
<-waitLock
exitCode := getExitCode(c)
cExitStatus, cerr := d.cleanupVM(c)
if cerr != nil {
waitErr = cerr
exitCode = cExitStatus
}
// check oom error
if oomKill {
exitCode = 137
}
return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: false}, waitErr
}
func pathExists(path string) bool {
if _, err := os.Stat(path); err == nil {
return true
}
return false
}
func pathExecutable(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
mode := s.Mode()
if mode&0111 != 0 {
return true
}
return false
}
func (d *driver) cleanupVM(c *execdriver.Command) (exitStatus int, err error) {
cmd := exec.Command("ip", "tuntap", "del", "dev", getTapIf(c), "mode", "tap")
var output []byte
if output, err = cmd.CombinedOutput(); err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
waitStatus := exitError.Sys().(syscall.WaitStatus)
exitStatus = waitStatus.ExitStatus()
}
logrus.Debugf("teardown failed for vm %s: %s (%s)", c.ID, string(output), err.Error())
}
// doesn't matter if this fails
// lkvm could have removed it, and stale sockets are not fatal
_ = os.Remove(fmt.Sprintf("%s/.lkvm/%s.sock", d.root, c.ID))
return exitStatus, err
}
// createContainer populates and configures the container type with the
// data provided by the execdriver.Command
func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
return execdriver.InitContainer(c), nil
}
/// Return the exit code of the process
// if the process has not exited -1 will be returned
func getExitCode(c *execdriver.Command) int {
if c.ProcessConfig.ProcessState == nil {
return -1
}
return c.ProcessConfig.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
}
func (d *driver) lkvmCommand(c *execdriver.Command, arg string) ([]byte, error) {
args := append([]string{lkvmName}, arg)
if c != nil {
args = append(args, "--name", c.ID)
}
cmd := exec.Command(lkvmName, args...)
cmd.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
return cmd.Output()
}
// Kill sends a signal to workload
func (d *driver) Kill(c *execdriver.Command, sig int) error {
// Not supported
return nil
}
func (d *driver) Pause(c *execdriver.Command) error {
_, err := d.lkvmCommand(c, "pause")
return err
}
func (d *driver) Unpause(c *execdriver.Command) error {
_, err := d.lkvmCommand(c, "resume")
return err
}
// Terminate forcibly stops a container
func (d *driver) Terminate(c *execdriver.Command) error {
_, err := d.lkvmCommand(c, "stop")
return err
}
func (d *driver) containerDir(containerID string) string {
return path.Join(d.libPath, "containers", containerID)
}
// isDigit returns true if s can be represented as an integer
func isDigit(s string) bool {
if _, err := strconv.Atoi(s); err == nil {
return true
}
return false
}
func (d *driver) getInfo(id string) ([]byte, error) {
output, err := d.lkvmCommand(nil, "list")
if err != nil {
return nil, err
}
for i, line := range strings.Split(string(output), "\n") {
if i < 2 {
continue
}
fields := strings.Fields(strings.TrimSpace(line))
if len(fields) != 3 {
continue
}
if !isDigit(fields[0]) {
continue
}
if fields[1] != id {
continue
}
return []byte(line), nil
}
return []byte(fmt.Sprintf("-1 %s stopped", id)), nil
}
type info struct {
ID string
driver *driver
}
func (i *info) IsRunning() bool {
output, err := i.driver.getInfo(i.ID)
if err != nil {
logrus.Errorf("Error getting info for %s container %s: %s (%s)",
driverName, i.ID, err, output)
return false
}
clrInfo, err := parseClrInfo(i.ID, string(output))
if err != nil {
return false
}
return clrInfo.Running
}
func (d *driver) Info(id string) execdriver.Info {
return &info{
ID: id,
driver: d,
}
}
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
// The VM doesn't expose the worload pid(s), so the only meaningful
// pid is that of the VM
return []int{d.containerPid}, nil
}
// TtyConsole is a type to represent a pseud-oterminal (see pty(7))
type TtyConsole struct {
MasterPty *os.File
SlavePty *os.File
}
// NewTtyConsole returns a new TtyConsole object.
func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) {
// lxc is special in that we cannot create the master outside of the container without
// opening the slave because we have nothing to provide to the cmd. We have to open both then do
// the crazy setup on command right now instead of passing the console path to lxc and telling it
// to open up that console. we save a couple of openfiles in the native driver because we can do
// this.
ptyMaster, ptySlave, err := pty.Open()
if err != nil {
return nil, err
}
tty := &TtyConsole{
MasterPty: ptyMaster,
SlavePty: ptySlave,
}
if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil {
tty.Close()
return nil, err
}
processConfig.Console = tty.SlavePty.Name()
return tty, nil
}
// Master returns the master end of the pty
func (t *TtyConsole) Master() *os.File {
return t.MasterPty
}
// Resize modifies the size of the pty terminal being used.
func (t *TtyConsole) Resize(h, w int) error {
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
}
// AttachPipes associates the specified pipes with the pty master.
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
command.Stdout = t.SlavePty
command.Stderr = t.SlavePty
go func() {
if wb, ok := pipes.Stdout.(interface {
CloseWriters() error
}); ok {
defer wb.CloseWriters()
}
io.Copy(pipes.Stdout, t.MasterPty)
}()
if pipes.Stdin != nil {
command.Stdin = t.SlavePty
command.SysProcAttr.Setctty = true
go func() {
io.Copy(t.MasterPty, pipes.Stdin)
pipes.Stdin.Close()
}()
}
return nil
}
// Close closes both ends of the pty.
func (t *TtyConsole) Close() error {
t.SlavePty.Close()
return t.MasterPty.Close()
}
func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
return -1, fmt.Errorf("Unsupported: Exec is not supported by the %q driver", driverName)
}
// Clean up after an Exec
func (d *driver) Clean(id string) error {
return nil
}
func (d *driver) generateEnvConfig(c *execdriver.Command) error {
data := []byte(strings.Join(c.ProcessConfig.Env, "\n"))
p := path.Join(d.libPath, "containers", c.ID, "config.env")
c.Mounts = append(c.Mounts, execdriver.Mount{
Source: p,
Destination: "/.dockerenv",
Writable: false,
Private: true,
})
return ioutil.WriteFile(p, data, 0600)
}
func (d *driver) generateDockerInit(c *execdriver.Command) error {
p := fmt.Sprintf("%s/.containerexec", c.Rootfs)
var args []string
if pathExecutable(p) {
return nil
}
args = append(args, c.ProcessConfig.Entrypoint)
args = append(args, c.ProcessConfig.Arguments...)
data := []byte(fmt.Sprintf("#!/bin/sh\n%s\n", strings.Join(args, " ")))
return ioutil.WriteFile(p, data, 0755)
}
func (d *driver) linkExists(name string) bool {
cmd := exec.Command("ip", "link", "show", name)
err := cmd.Run()
return err == nil
}
func (d *driver) setupNetwork(c *execdriver.Command) error {
ifname := getTapIf(c)
var bridgeName string
var bridgeLinkName string
var output []byte
var err error
var ok bool
bridge := c.NetworkSettings.Networks["bridge"]
if bridge == nil {
return fmt.Errorf("no bridge network available")
}
d.ipaddr = bridge.IPAddress
d.macaddr = bridge.MacAddress
d.gateway = bridge.Gateway
// Extract the bridge details from the relevant endpoint.
for _, info := range c.EndpointInfo {
if _, ok = info[netlabel.BridgeEID].(string); !ok {
continue
}
if bridgeName, ok = info[netlabel.BridgeName].(string); !ok {
return fmt.Errorf("unable to determine bridge name")
}
if bridgeLinkName, ok = info[netlabel.BridgeLinkName].(string); !ok {
return fmt.Errorf("unable to determine bridge link name")
}
}
// Strip existing veth if it exists
if bridgeLinkName != "" && d.linkExists(bridgeLinkName) {
cmd := exec.Command("ip", "link", "del", bridgeLinkName)
if output, err = cmd.CombinedOutput(); err != nil {
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
return err
}
}
cmd := exec.Command("ip", "tuntap", "add", "dev", ifname, "mode", "tap", "vnet_hdr")
if output, err = cmd.CombinedOutput(); err != nil {
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
return err
}
cmd = exec.Command("ip", "link", "set", "dev", ifname, "master", bridgeName)
if output, err = cmd.CombinedOutput(); err != nil {
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
return err
}
cmd = exec.Command("ip", "link", "set", "dev", ifname, "up")
if output, err = cmd.CombinedOutput(); err != nil {
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
return err
}
return err
}
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
if _, ok := d.activeContainers[id]; !ok {
return nil, fmt.Errorf("%s is not a key in active containers", id)
}
// FIXME:
return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
}
func (d *driver) SupportsHooks() bool {
return false
}
+51
View File
@@ -0,0 +1,51 @@
// +build linux
package clr
import (
"errors"
"strconv"
"strings"
)
var (
ErrCannotParse = errors.New("cannot parse raw input")
)
type clrInfo struct {
Running bool
Pid int
}
func parseClrInfo(name, raw string) (*clrInfo, error) {
if raw == "" {
return nil, ErrCannotParse
}
var (
err error
info = &clrInfo{}
)
fields := strings.Fields(strings.TrimSpace(raw))
// The format is expected to be:
//
// <pid> <name> <state>
//
if len(fields) != 3 {
return nil, ErrCannotParse
}
info.Pid, err = strconv.Atoi(fields[0])
if err != nil {
return nil, ErrCannotParse
}
if fields[1] != name {
return nil, ErrCannotParse
}
info.Running = fields[2] == "running"
return info, nil
}
+97
View File
@@ -0,0 +1,97 @@
// +build linux
package clr
import (
"encoding/json"
"flag"
"fmt"
"os"
"strings"
"syscall"
)
// Args provided to the init function for a driver
type InitArgs struct {
User string
Gateway string
Ip string
WorkDir string
Privileged bool
Env []string
Args []string
Mtu int
Console string
Pipe int
Root string
CapAdd string
CapDrop string
}
func getArgs() *InitArgs {
var (
// Get cmdline arguments
user = flag.String("u", "", "username or uid")
gateway = flag.String("g", "", "gateway address")
ip = flag.String("i", "", "ip address")
workDir = flag.String("w", "", "workdir")
privileged = flag.Bool("privileged", false, "privileged mode")
mtu = flag.Int("mtu", 1500, "interface mtu")
capAdd = flag.String("cap-add", "", "capabilities to add")
capDrop = flag.String("cap-drop", "", "capabilities to drop")
)
flag.Parse()
return &InitArgs{
User: *user,
Gateway: *gateway,
Ip: *ip,
WorkDir: *workDir,
Privileged: *privileged,
Args: flag.Args(),
Mtu: *mtu,
CapAdd: *capAdd,
CapDrop: *capDrop,
}
}
// Clear environment pollution introduced by lxc-start
func setupEnv(args *InitArgs) error {
// Get env
var env []string
dockerenv, err := os.Open(".dockerenv")
if err != nil {
return fmt.Errorf("Unable to load environment variables: %v", err)
}
defer dockerenv.Close()
if err := json.NewDecoder(dockerenv).Decode(&env); err != nil {
return fmt.Errorf("Unable to decode environment variables: %v", err)
}
// Propagate the plugin-specific container env variable
env = append(env, "container="+os.Getenv("container"))
args.Env = env
os.Clearenv()
for _, kv := range args.Env {
parts := strings.SplitN(kv, "=", 2)
if len(parts) == 1 {
parts = append(parts, "")
}
os.Setenv(parts[0], parts[1])
}
return nil
}
// Setup working directory
func setupWorkingDirectory(args *InitArgs) error {
if args.WorkDir == "" {
return nil
}
if err := syscall.Chdir(args.WorkDir); err != nil {
return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
}
return nil
}
+6
View File
@@ -8,7 +8,9 @@ import (
"github.com/docker/docker/pkg/idtools"
// TODO Windows: Factor out ulimit
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/pkg/ulimit"
"github.com/docker/docker/runconfig"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/configs"
)
@@ -230,4 +232,8 @@ type Command struct {
LayerPaths []string `json:"layer_paths"` // Windows needs to know the layer paths and folder for a command
LayerFolder string `json:"layer_folder"`
Hostname string `json:"hostname"` // Windows sets the hostname in the execdriver
NetworkSettings *network.Settings `json:"network_settings"`
EndpointInfo []map[string]interface{} `json:"endpoint_info"`
HostConfig *runconfig.HostConfig `json:"hostconfig"`
}
@@ -8,6 +8,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/daemon/execdriver/clr"
"github.com/docker/docker/daemon/execdriver/lxc"
"github.com/docker/docker/daemon/execdriver/native"
"github.com/docker/docker/pkg/sysinfo"
@@ -15,7 +16,10 @@ import (
// NewDriver returns a new execdriver.Driver from the given name configured with the provided options.
func NewDriver(name string, options []string, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
rootPath := path.Join(root, "execdriver", name)
switch name {
case "clr":
return clr.NewDriver(rootPath, libPath, initPath, sysInfo.AppArmor)
case "lxc":
// we want to give the lxc driver the full docker root because it needs
// to access and write config and template files in /var/lib/docker/containers/*
@@ -23,7 +27,7 @@ func NewDriver(name string, options []string, root, libPath, initPath string, sy
logrus.Warn("LXC built-in support is deprecated.")
return lxc.NewDriver(root, libPath, initPath, sysInfo.AppArmor)
case "native":
return native.NewDriver(path.Join(root, "execdriver", "native"), initPath, options)
return native.NewDriver(rootPath, initPath, options)
}
return nil, fmt.Errorf("unknown exec driver %s", name)
}
+72 -7
View File
@@ -3,6 +3,7 @@
package devmapper
import (
"bufio"
"encoding/json"
"errors"
"fmt"
@@ -148,6 +149,8 @@ type Status struct {
Metadata DiskUsage
// BaseDeviceSize is base size of container and image
BaseDeviceSize uint64
// BaseDeviceFS is backing filesystem.
BaseDeviceFS string
// SectorSize size of the vector.
SectorSize uint64
// UdevSyncSupported is true if sync is supported.
@@ -532,6 +535,45 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bo
return devicemapper.ActivateDevice(devices.getPoolDevName(), info.Name(), info.DeviceID, info.Size)
}
// Return true only if kernel supports xfs and mkfs.xfs is available
func xfsSupported() bool {
// Make sure mkfs.xfs is available
if _, err := exec.LookPath("mkfs.xfs"); err != nil {
return false
}
// Check if kernel supports xfs filesystem or not.
exec.Command("modprobe", "xfs").Run()
f, err := os.Open("/proc/filesystems")
if err != nil {
logrus.Warnf("Could not check if xfs is supported: %v", err)
return false
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if strings.HasSuffix(s.Text(), "\txfs") {
return true
}
}
if err := s.Err(); err != nil {
logrus.Warnf("Could not check if xfs is supported: %v", err)
}
return false
}
func determineDefaultFS() string {
if xfsSupported() {
return "xfs"
}
logrus.Warn("XFS is not supported in your system. Either the kernel doesnt support it or mkfs.xfs is not in your PATH. Defaulting to ext4 filesystem")
return "ext4"
}
func (devices *DeviceSet) createFilesystem(info *devInfo) error {
devname := info.DevName()
@@ -543,6 +585,11 @@ func (devices *DeviceSet) createFilesystem(info *devInfo) error {
args = append(args, devname)
var err error
if devices.filesystem == "" {
devices.filesystem = determineDefaultFS()
}
switch devices.filesystem {
case "xfs":
err = exec.Command("mkfs.xfs", args...).Run()
@@ -844,7 +891,11 @@ func (devices *DeviceSet) getBaseDeviceSize() uint64 {
return info.Size
}
func (devices *DeviceSet) verifyBaseDeviceUUID(baseInfo *devInfo) error {
func (devices *DeviceSet) getBaseDeviceFS() string {
return devices.filesystem
}
func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error {
devices.Lock()
defer devices.Unlock()
@@ -860,9 +911,23 @@ func (devices *DeviceSet) verifyBaseDeviceUUID(baseInfo *devInfo) error {
}
if devices.BaseDeviceUUID != uuid {
return fmt.Errorf("Current Base Device UUID:%s does not match with stored UUID:%s", uuid, devices.BaseDeviceUUID)
return fmt.Errorf("Current Base Device UUID:%s does not match with stored UUID:%s. Possibly using a different thin pool than last invocation", uuid, devices.BaseDeviceUUID)
}
// If user specified a filesystem using dm.fs option and current
// file system of base image is not same, warn user that dm.fs
// will be ignored.
if devices.filesystem != "" {
fs, err := ProbeFsType(baseInfo.DevName())
if err != nil {
return err
}
if fs != devices.filesystem {
logrus.Warnf("Base device already exists and has filesystem %s on it. User specified filesystem %s will be ignored.", fs, devices.filesystem)
devices.filesystem = fs
}
}
return nil
}
@@ -963,7 +1028,7 @@ func (devices *DeviceSet) checkThinPool() error {
// Base image is initialized properly. Either save UUID for first time (for
// upgrade case or verify UUID.
func (devices *DeviceSet) setupVerifyBaseImageUUID(baseInfo *devInfo) error {
func (devices *DeviceSet) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error {
// If BaseDeviceUUID is nil (upgrade case), save it and return success.
if devices.BaseDeviceUUID == "" {
if err := devices.saveBaseDeviceUUID(baseInfo); err != nil {
@@ -972,8 +1037,8 @@ func (devices *DeviceSet) setupVerifyBaseImageUUID(baseInfo *devInfo) error {
return nil
}
if err := devices.verifyBaseDeviceUUID(baseInfo); err != nil {
return fmt.Errorf("Base Device UUID verification failed. Possibly using a different thin pool than last invocation:%v", err)
if err := devices.verifyBaseDeviceUUIDFS(baseInfo); err != nil {
return fmt.Errorf("Base Device UUID and Filesystem verification failed.%v", err)
}
return nil
@@ -988,7 +1053,7 @@ func (devices *DeviceSet) setupBaseImage() error {
if oldInfo != nil {
if oldInfo.Initialized && !oldInfo.Deleted {
if err := devices.setupVerifyBaseImageUUID(oldInfo); err != nil {
if err := devices.setupVerifyBaseImageUUIDFS(oldInfo); err != nil {
return err
}
@@ -2210,6 +2275,7 @@ func (devices *DeviceSet) Status() *Status {
status.DeferredDeleteEnabled = devices.deferredDelete
status.DeferredDeletedDeviceCount = devices.nrDeletedDevices
status.BaseDeviceSize = devices.getBaseDeviceSize()
status.BaseDeviceFS = devices.getBaseDeviceFS()
totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus()
if err == nil {
@@ -2270,7 +2336,6 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
metaDataLoopbackSize: defaultMetaDataLoopbackSize,
baseFsSize: defaultBaseFsSize,
overrideUdevSyncCheck: defaultUdevSyncOverride,
filesystem: "ext4",
doBlkDiscard: true,
thinpBlockSize: defaultThinpBlockSize,
deviceIDMap: make([]byte, deviceIDMapSz),
+1 -1
View File
@@ -80,7 +80,7 @@ func (d *Driver) Status() [][2]string {
{"Pool Name", s.PoolName},
{"Pool Blocksize", fmt.Sprintf("%s", units.HumanSize(float64(s.SectorSize)))},
{"Base Device Size", fmt.Sprintf("%s", units.HumanSize(float64(s.BaseDeviceSize)))},
{"Backing Filesystem", backingFs},
{"Backing Filesystem", s.BaseDeviceFS},
{"Data file", s.DataFile},
{"Metadata file", s.MetadataFile},
{"Data Space Used", fmt.Sprintf("%s", units.HumanSize(float64(s.Data.Used)))},
+22 -8
View File
@@ -9,6 +9,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/graph"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/graphdb"
"github.com/docker/docker/pkg/nat"
@@ -285,6 +286,24 @@ func includeContainerInList(container *Container, ctx *listContext) iterationAct
return includeContainer
}
func getImage(s *graph.TagStore, img, imgID string) (string, error) {
// both Image and ImageID is actually ids, nothing to guess
if strings.HasPrefix(imgID, img) {
return img, nil
}
id, err := s.GetID(img)
if err != nil {
if err == graph.ErrNameIsNotExist {
return imgID, nil
}
return "", err
}
if id != imgID {
return imgID, nil
}
return img, nil
}
// transformContainer generates the container type expected by the docker ps command.
func (daemon *Daemon) transformContainer(container *Container, ctx *listContext) (*types.Container, error) {
newC := &types.Container{
@@ -297,16 +316,11 @@ func (daemon *Daemon) transformContainer(container *Container, ctx *listContext)
newC.Names = []string{}
}
img, err := daemon.repositories.LookupImage(container.Config.Image)
showImg, err := getImage(daemon.repositories, container.Config.Image, container.ImageID)
if err != nil {
// If the image can no longer be found by its original reference,
// it makes sense to show the ID instead of a stale reference.
newC.Image = container.ImageID
} else if container.ImageID == img.ID {
newC.Image = container.Config.Image
} else {
newC.Image = container.ImageID
return nil, err
}
newC.Image = showImg
if len(container.Args) > 0 {
args := []string{}
+26
View File
@@ -142,6 +142,32 @@ func (m *containerMonitor) Start() error {
m.lastStartTime = time.Now()
// Make the network settings available to the execution
// driver to allow for integration with libnetwork networking.
m.container.command.NetworkSettings = m.container.NetworkSettings
// Allow the execution driver to query memory limits
m.container.command.HostConfig = m.container.hostConfig
// Make the network endpoint details available to the execution
// driver, to allow it to determine the bridge name.
for name, _ := range m.container.NetworkSettings.Networks {
n, _err := m.container.daemon.netController.NetworkByName(name)
if _err == nil {
var eps []map[string]interface{}
for _, ep := range n.Endpoints() {
info, err := ep.DriverInfo()
if err != nil {
continue
}
eps = append(eps, info)
}
m.container.command.EndpointInfo = eps
}
}
if exitStatus, err = m.container.daemon.run(m.container, pipes, m.callback); err != nil {
// if we receive an internal error from the initial start of a container then lets
// return it instead of entering the restart loop
-40
View File
@@ -8,8 +8,6 @@ import (
"github.com/docker/docker/api/types/versions/v1p20"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/pkg/version"
lntypes "github.com/docker/libnetwork/types"
"github.com/opencontainers/runc/libcontainer"
)
// ContainerStatsConfig holds information for configuring the runtime
@@ -50,10 +48,6 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
var preCPUStats types.CPUStats
getStatJSON := func(v interface{}) *types.StatsJSON {
update := v.(*execdriver.ResourceStats)
// Retrieve the nw statistics from libnetwork and inject them in the Stats
if nwStats, err := daemon.getNetworkStats(container); err == nil {
update.Stats.Interfaces = nwStats
}
ss := convertStatsToAPITypes(update.Stats)
ss.PreCPUStats = preCPUStats
ss.MemoryStats.Limit = uint64(update.MemoryLimit)
@@ -133,37 +127,3 @@ func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStats
}
}
}
func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) {
var list []*libcontainer.NetworkInterface
sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
if err != nil {
return list, err
}
stats, err := sb.Statistics()
if err != nil {
return list, err
}
// Convert libnetwork nw stats into libcontainer nw stats
for ifName, ifStats := range stats {
list = append(list, convertLnNetworkStats(ifName, ifStats))
}
return list, nil
}
func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
n := &libcontainer.NetworkInterface{Name: name}
n.RxBytes = stats.RxBytes
n.RxPackets = stats.RxPackets
n.RxErrors = stats.RxErrors
n.RxDropped = stats.RxDropped
n.TxBytes = stats.TxBytes
n.TxPackets = stats.TxPackets
n.TxErrors = stats.TxErrors
n.TxDropped = stats.TxDropped
return n
}
+41
View File
@@ -14,6 +14,8 @@ import (
"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/pubsub"
lntypes "github.com/docker/libnetwork/types"
"github.com/opencontainers/runc/libcontainer"
"github.com/opencontainers/runc/libcontainer/system"
)
@@ -118,6 +120,11 @@ func (s *statsCollector) run() {
continue
}
stats.SystemUsage = systemUsage
// Retrieve the nw statistics from libnetwork and inject them in the Stats
if nwStats, err := s.getNetworkStats(pair.container); err == nil {
stats.Interfaces = nwStats
}
pair.publisher.Publish(stats)
}
}
@@ -170,3 +177,37 @@ func (s *statsCollector) getSystemCPUUsage() (uint64, error) {
}
return 0, derr.ErrorCodeBadStatFormat
}
func (s *statsCollector) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) {
var list []*libcontainer.NetworkInterface
sb, err := c.daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
if err != nil {
return list, err
}
stats, err := sb.Statistics()
if err != nil {
return list, err
}
// Convert libnetwork nw stats into libcontainer nw stats
for ifName, ifStats := range stats {
list = append(list, convertLnNetworkStats(ifName, ifStats))
}
return list, nil
}
func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
n := &libcontainer.NetworkInterface{Name: name}
n.RxBytes = stats.RxBytes
n.RxPackets = stats.RxPackets
n.RxErrors = stats.RxErrors
n.RxDropped = stats.RxDropped
n.TxBytes = stats.TxBytes
n.TxPackets = stats.TxPackets
n.TxErrors = stats.TxErrors
n.TxDropped = stats.TxDropped
return n
}
+4 -3
View File
@@ -365,9 +365,10 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
}
}
shared := label.IsShared(bind.Mode)
if err := label.Relabel(bind.Source, container.MountLabel, shared); err != nil {
return err
if label.RelabelNeeded(bind.Mode) {
if err := label.Relabel(bind.Source, container.MountLabel, label.IsShared(bind.Mode)); err != nil {
return err
}
}
binds[bind.Destination] = true
mountPoints[bind.Destination] = bind
+14 -1
View File
@@ -12,7 +12,8 @@ weight=-1
# Understand Docker plugins
You can extend the capabilities of the Docker Engine by loading third-party
plugins.
plugins. This page explains the types of plugins and provides links to several
volume and network plugins for Docker.
## Types of plugins
@@ -64,6 +65,18 @@ The following plugins exist:
which is written in Go and provides advanced storage functionality for many
platforms including EC2, OpenStack, XtremIO, and ScaleIO.
* The [Contiv Volume Plugin](https://github.com/contiv/volplugin) is an open
source volume plugin that provides multi-tenant, persistent, distributed storage
with intent based consumption using ceph underneath.
* The [Contiv Networking](https://github.com/contiv/netplugin) is an open source
libnetwork plugin to provide infrastructure and security policies for a
multi-tenant micro services deployment, while providing an integration to
physical network for non-container workload. Contiv Networking implements the
remote driver and IPAM APIs available in Docker 1.9 onwards.
* The [Weave Network Plugin](https://github.com/weaveworks/docker-plugin) creates a virtual network that connects your Docker containers - across multiple hosts or clouds and enables automatic discovery of applications. Weave networks are resilient, partition tolerant, secure and work in partially connected networks, and other adverse environments - all configured with delightful simplicity.
## Troubleshooting a plugin
If you are having problems with Docker after loading a plugin, ask the authors
-1
View File
@@ -5,7 +5,6 @@ description = "Network driver plugins."
keywords = ["Examples, Usage, plugins, docker, documentation, user guide"]
[menu.main]
parent = "mn_extend"
weight=-1
+++
<![end-metadata]-->
+1 -1
View File
@@ -51,7 +51,7 @@ from the new repository:
4. Add the new `gpg` key.
$ apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
$ apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
5. Open the `/etc/apt/sources.list.d/docker.list` file in your favorite editor.
+26 -7
View File
@@ -55,7 +55,7 @@ packages from the new repository:
3. Add the new `gpg` key.
$ sudo apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
4. Open the `/etc/apt/sources.list.d/docker.list` file in your favorite editor.
@@ -67,13 +67,13 @@ packages from the new repository:
The possible entries are:
# Ubuntu Precise
# Ubuntu Precise 12.04 (LTS)
deb https://apt.dockerproject.org/repo ubuntu-precise main
# Ubuntu Trusty
# Ubuntu Trusty 14.04 (LTS)
deb https://apt.dockerproject.org/repo ubuntu-trusty main
# Ubuntu Vivid
# Ubuntu Vivid 15.04
deb https://apt.dockerproject.org/repo ubuntu-vivid main
# Ubuntu Wily
# Ubuntu Wily 15.10
deb https://apt.dockerproject.org/repo ubuntu-wily main
7. Save and close the `/etc/apt/sources.list.d/docker.list` file.
@@ -94,12 +94,31 @@ packages from the new repository:
### Prerequisites by Ubuntu Version
The following Ubuntu versions have no additional prerequisites:
- Ubuntu Wily 15.10
- Ubuntu Vivid 15.04
- Ubuntu Trusty 14.04 (LTS)
For Ubuntu Trusty, Vivid, and Wily, it's recommended to install the
`linux-image-extra` kernel package. The `linux-image-extra` package
allows you use the `aufs` storage driver.
To install the `linux-image-extra` package for your kernel version:
1. Open a terminal on your Ubuntu host.
2. Update your package manager.
$ sudo apt-get update
3. Install the recommended package.
$ sudo apt-get install linux-image-extra-$(uname -r)
4. Go ahead and install Docker.
#### Ubuntu Precise 12.04 (LTS)
For Ubuntu Precise, Docker requires the 3.13 kernel version. If your kernel
version is older than 3.13, you must upgrade it. Refer to this table to see
which packages are required for your environment:
+1 -1
View File
@@ -124,7 +124,7 @@ list of DNS options to be used in the container.
### v1.20 API changes
[Docker Remote API v1.20](docker_remote_api_v1.20/) documentation
[Docker Remote API v1.20](docker_remote_api_v1.20.md) documentation
* `GET /containers/(id)/archive` get an archive of filesystem content from a container.
* `PUT /containers/(id)/archive` upload an archive of content to be extracted to
@@ -15,7 +15,7 @@ weight = 7
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST, but for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
`STDIN` and `STDERR`.
@@ -15,7 +15,7 @@ weight = 6
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST, but for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
`STDIN` and `STDERR`.
@@ -15,7 +15,7 @@ weight = 5
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST, but for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
`STDIN` and `STDERR`.
@@ -15,7 +15,7 @@ weight = 4
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST, but for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
`STDIN` and `STDERR`.
@@ -15,7 +15,7 @@ weight = 3
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST, but for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `STDOUT`,
`STDIN` and `STDERR`.
@@ -15,7 +15,7 @@ weight = 2
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST. However, for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `stdout`,
`stdin` and `stderr`.
@@ -15,7 +15,7 @@ weight = 1
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST. However, for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `stdout`,
`stdin` and `stderr`.
@@ -15,7 +15,7 @@ weight=-2
- The Remote API has replaced `rcli`.
- The daemon listens on `unix:///var/run/docker.sock` but you can
[Bind Docker to another host/port or a Unix socket](../../articles/basics.md#bind-docker-to-another-hostport-or-a-unix-socket).
[Bind Docker to another host/port or a Unix socket](../../userguide/basics.md#bind-docker-to-another-host-port-or-a-unix-socket).
- The API tends to be REST. However, for some complex commands, like `attach`
or `pull`, the HTTP connection is hijacked to transport `stdout`,
`stdin` and `stderr`.
+1 -1
View File
@@ -83,7 +83,7 @@ This is useful when you want to set up a container configuration ahead of time
so that it is ready to start when you need it. The initial status of the
new container is `created`.
Please see the [run command](run.md) section and the [Docker run reference](run.md) for more details.
Please see the [run command](run.md) section and the [Docker run reference](../run.md) for more details.
## Examples
+2 -2
View File
@@ -261,11 +261,11 @@ options for `zfs` start with `zfs`.
* `dm.fs`
Specifies the filesystem type to use for the base device. The supported
options are "ext4" and "xfs". The default is "ext4"
options are "ext4" and "xfs". The default is "xfs"
Example use:
$ docker daemon --storage-opt dm.fs=xfs
$ docker daemon --storage-opt dm.fs=ext4
* `dm.mkfsarg`
+6 -5
View File
@@ -21,11 +21,12 @@ parent = "smn_cli"
Creates a new volume that containers can consume and store data in. If a name is not specified, Docker generates a random name. You create a volume and then configure the container to use it, for example:
$ docker volume create --name hello
hello
$ docker run -d -v hello:/world busybox ls /world
$ docker volume create --name hello
hello
The mount is created inside the container's `/src` directory. Docker does not support relative paths for mount points inside the container.
$ docker run -d -v hello:/world busybox ls /world
The mount is created inside the container's `/world` directory. Docker does not support relative paths for mount points inside the container.
Multiple containers can use the same volume in the same time period. This is useful if two containers need access to shared data. For example, if one container writes and the other reads the data.
@@ -41,7 +42,7 @@ If you specify a volume name already in use on the current driver, Docker assume
Some volume drivers may take options to customize the volume creation. Use the `-o` or `--opt` flags to pass driver options:
$ docker volume create --driver fake --opt tardis=blue --opt timey=wimey
$ docker volume create --driver fake --opt tardis=blue --opt timey=wimey
These options are passed directly to the volume driver. Options for
different volume drivers may do different things (or nothing at all).
+1 -1
View File
@@ -37,4 +37,4 @@ Example output:
]
$ docker volume inspect --format '{{ .Mountpoint }}' 85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
"/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data"
/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data
+2 -2
View File
@@ -18,5 +18,5 @@ parent = "smn_cli"
Removes one or more volumes. You cannot remove a volume that is in use by a container.
$ docker volume rm hello
hello
$ docker volume rm hello
hello
+16 -6
View File
@@ -404,16 +404,26 @@ provision the hosts are with Docker Machine.
![Engine on each host](images/engine_on_net.png)
You should open the following ports between each of your hosts.
| Protocol | Port | Description |
|----------|------|-----------------------|
| udp | 4789 | Data plane (VXLAN) |
| tcp/udp | 7946 | Control plane |
Your key-value store service may require additional ports.
Check your vendor's documentation and open any required ports.
Once you have several machines provisioned, you can use Docker Swarm to quickly
form them into a swarm which includes a discovery service as well.
To create an overlay network, you configure options on the `daemon` on each
Docker Engine for use with `overlay` network. There are two options to set:
| Option | Description |
|----------------------------------|-----------------------------------------------------------|
| `--cluster-store=PROVIDER://URL` | Describes the location of the KV service. |
| `--cluster-advertise=HOST_IP` | Advertises containers created by the HOST on the network. |
| Option | Description |
|-----------------------------------------------|-------------------------------------------------------------|
| `--cluster-store=PROVIDER://URL` | Describes the location of the KV service. |
| `--cluster-advertise=HOST_IP|HOST_IFACE:PORT` | The IP address or interface of the HOST used for clustering |
Create an `overlay` network on one of the machines in the Swarm.
@@ -426,7 +436,7 @@ provides complete isolation for the containers.
Then, on each host, launch containers making sure to specify the network name.
$ docker run -itd --net=mmy-multi-host-network busybox
$ docker run -itd --net=my-multi-host-network busybox
Once connected, each container has access to all the containers in the network
regardless of which Docker host the container was launched on.
@@ -477,4 +487,4 @@ and removed in a future release.
- [Managing Data in Containers](../dockervolumes.md)
- [Docker Machine overview](https://docs.docker.com/machine)
- [Docker Swarm overview](https://docs.docker.com/swarm)
- [Investigate the LibNetwork project](https://github.com/docker/libnetwork/blob/master)
- [Investigate the LibNetwork project](https://github.com/docker/libnetwork)
@@ -7,13 +7,13 @@ keywords = ["Examples, Usage, network, docker, documentation, user guide, multih
parent = "smn_networking"
weight=-3
+++
<![end-metadata]-->
<![end-metadata]-->
# Get started with multi-host networking
This article uses an example to explain the basics of creating a multi-host
network. Docker Engine supports multi-host-networking out-of-the-box through the
`overlay` network driver. Unlike `bridge` networks overlay networks require
network. Docker Engine supports multi-host networking out-of-the-box through the
`overlay` network driver. Unlike `bridge` networks, overlay networks require
some pre-existing conditions before you can create one. These conditions are:
* A host with a 3.16 kernel version or higher.
@@ -22,8 +22,8 @@ some pre-existing conditions before you can create one. These conditions are:
* A properly configured Engine `daemon` on each host in the cluster.
Though Docker Machine and Docker Swarm are not mandatory to experience Docker
multi-host-networking, this example uses them to illustrate how they are
integrated. You'll use Machine to create both the the key-value store
multi-host networking, this example uses them to illustrate how they are
integrated. You'll use Machine to create both the key-value store
server and the host cluster. This example creates a Swarm cluster.
## Prerequisites
@@ -39,10 +39,10 @@ Machine to the latest versions.
## Step 1: Set up a key-value store
An overlay network requires a key-value store. The key-value stores information
about the network state which includes discovery, networks, endpoints,
ip-addresses, and more. Docker supports Consul, Etcd, and ZooKeeper (Distributed
store) key-value stores. This example uses Consul.
An overlay network requires a key-value store. The key-value store holds
information about the network state which includes discovery, networks,
endpoints, IP addresses, and more. Docker supports Consul, Etcd, and ZooKeeper
key-value stores. This example uses Consul.
1. Log into a system prepared with the prerequisite Docker Engine, Docker Machine, and VirtualBox software.
@@ -62,9 +62,10 @@ store) key-value stores. This example uses Consul.
-h "consul" \
progrium/consul -server -bootstrap
You passed the `docker run` command the connection configuration using a bash
expansion `$(docker-machine config mh-keystore)`. The client started a
`progrium/consul` image running in the `mh-keystore` machine. The server is called `consul`and is listening port `8500`.
A bash expansion `$(docker-machine config mh-keystore)` is used to pass the
connection configuration to the `docker run` command. The client starts a
`progrium/consul` image running in the `mh-keystore` machine. The server is
called `consul` and is listening on port `8500`.
4. Set your local environment to the `mh-keystore` machine.
@@ -82,7 +83,7 @@ Keep your terminal open and move onto the next step.
## Step 2: Create a Swarm cluster
In this step, you use `docker-machine` to provision the hosts for your network.
At this point, you won't actually created the network. You'll create several
At this point, you won't actually create the network. You'll create several
machines in VirtualBox. One of the machines will act as the Swarm master;
you'll create that first. As you create each host, you'll pass the Engine on
that machine options that are needed by the `overlay` network driver.
@@ -91,7 +92,7 @@ that machine options that are needed by the `overlay` network driver.
$ docker-machine create \
-d virtualbox \
--swarm --swarm-image="swarm" --swarm-master \
--swarm --swarm-master \
--swarm-discovery="consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
@@ -102,7 +103,7 @@ that machine options that are needed by the `overlay` network driver.
2. Create another host and add it to the Swarm cluster.
$ docker-machine create -d virtualbox \
--swarm --swarm-image="swarm:1.0.0-rc2" \
--swarm \
--swarm-discovery="consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
@@ -112,14 +113,13 @@ that machine options that are needed by the `overlay` network driver.
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM
default virtualbox Running tcp://192.168.99.100:2376
mh-keystore virtualbox Running tcp://192.168.99.103:2376
mhs-demo0 virtualbox Running tcp://192.168.99.104:2376 mhs-demo0 (master)
mhs-demo1 virtualbox Running tcp://192.168.99.105:2376 mhs-demo0
default - virtualbox Running tcp://192.168.99.100:2376
mh-keystore * virtualbox Running tcp://192.168.99.103:2376
mhs-demo0 - virtualbox Running tcp://192.168.99.104:2376 mhs-demo0 (master)
mhs-demo1 - virtualbox Running tcp://192.168.99.105:2376 mhs-demo0
At this point you have a set of hosts running on your network. You are ready to create a multi-host network for containers using these hosts.
Leave your terminal open and go onto the next step.
## Step 3: Create the overlay Network
@@ -155,7 +155,7 @@ To create an overlay network
Total Memory: 2.043 GiB
Name: 30438ece0915
From this information, you can see that you are running three containers and 2 images on the Master.
From this information, you can see that you are running three containers and two images on the Master.
3. Create your `overlay` network.
@@ -167,54 +167,51 @@ To create an overlay network
$ docker network ls
NETWORK ID NAME DRIVER
412c2496d0eb mhs-demo1/host host
dd51763e6dd2 mhs-demo0/bridge bridge
6b07d0be843f my-net overlay
b4234109bd9b mhs-demo0/none null
1aeead6dd890 mhs-demo0/host host
d0bb78cbe7bd mhs-demo1/bridge bridge
1c0eb8f69ebb mhs-demo1/none null
412c2496d0eb mhs-demo1/host host
dd51763e6dd2 mhs-demo0/bridge bridge
6b07d0be843f my-net overlay
b4234109bd9b mhs-demo0/none null
1aeead6dd890 mhs-demo0/host host
d0bb78cbe7bd mhs-demo1/bridge bridge
1c0eb8f69ebb mhs-demo1/none null
Because you are in the Swarm master environment, you see all the networks on all Swarm agents. Notice that each `NETWORK ID` is unique. The default networks on each engine and the single overlay network.
As you are in the Swarm master environment, you see all the networks on all
the Swarm agents: the default networks on each engine and the single overlay
network. Notice that each `NETWORK ID` is unique.
5. Switch to each Swarm agent in turn and list the network.
5. Switch to each Swarm agent in turn and list the networks.
$ eval $(docker-machine env mhs-demo0)
$ docker network ls
NETWORK ID NAME DRIVER
6b07d0be843f my-net overlay
dd51763e6dd2 bridge bridge
b4234109bd9b none null
1aeead6dd890 host host
6b07d0be843f my-net overlay
dd51763e6dd2 bridge bridge
b4234109bd9b none null
1aeead6dd890 host host
$ eval $(docker-machine env mhs-demo1)
$ docker network ls
NETWORK ID NAME DRIVER
d0bb78cbe7bd bridge bridge
1c0eb8f69ebb none null
412c2496d0eb host host
6b07d0be843f my-net overlay
d0bb78cbe7bd bridge bridge
1c0eb8f69ebb none null
412c2496d0eb host host
6b07d0be843f my-net overlay
Both agents reports it has the `my-net `network with the `6b07d0be843f` id. You have a multi-host container network running!
Both agents report they have the `my-net` network with the `6b07d0be843f` ID.
You now have a multi-host container network running!
## Step 4: Run an application on your Network
Once your network is created, you can start a container on any of the hosts and it automatically is part of the network.
1. Point your environment to your `mhs-demo0` instance.
1. Point your environment to the Swarm master.
$ eval $(docker-machine env mhs-demo0)
$ eval $(docker-machine env --swarm mhs-demo0)
2. Start an Nginx server on `mhs-demo0`.
2. Start an Nginx web server on the `mhs-demo0` instance.
$ docker run -itd --name=web --net=my-net --env="constraint:node==mhs-demo0" nginx
This command starts a web server on the Swarm master.
3. Point your Machine environment to `mhs-demo1`
$ eval $(docker-machine env mhs-demo1)
4. Run a Busybox instance and get the contents of the Ngnix server's home page.
4. Run a BusyBox instance on the `mhs-demo1` instance and get the contents of the Nginx server's home page.
$ docker run -it --rm --net=my-net --env="constraint:node==mhs-demo1" busybox wget -O- http://web
Unable to find image 'busybox:latest' locally
@@ -284,7 +281,7 @@ to have external connectivity outside of their cluster.
412c2496d0eb host host
97102a22e8d2 docker_gwbridge bridge
2. Check the Ngnix container's network interfaces.
2. Check the Nginx container's network interfaces.
$ docker exec web ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
@@ -314,9 +311,11 @@ to have external connectivity outside of their cluster.
You can try starting a second network on your existing Swarm cluster using Docker Compose.
1. Log into the Swarm master.
1. If you haven't already, install Docker Compose.
2. Install Docker Compose.
2. Change your environment to the Swarm master.
$ eval $(docker-machine env --swarm mhs-demo0)
3. Create a `docker-compose.yml` file.
@@ -326,7 +325,7 @@ You can try starting a second network on your existing Swarm cluster using Docke
image: bfirsh/compose-mongodb-demo
environment:
- "MONGO_HOST=counter_mongo_1"
- "constraint:node==swl-demo0"
- "constraint:node==mhs-demo0"
ports:
- "80:5000"
mongo:
@@ -336,7 +335,15 @@ You can try starting a second network on your existing Swarm cluster using Docke
6. Start the application with Compose.
$ docker-compose up --x-networking up -d
$ docker-compose --x-networking --project-name=counter up -d
7. Get the Swarm master's IP address.
$ docker-machine ip mhs-demo0
8. Put the IP address into your web browser.
Upon success, the browser should display the web application.
## Related information
+1 -1
View File
@@ -193,5 +193,5 @@ reason, you may want to place heavy write workloads on data volumes.
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [Select a storage driver](selectadriver.md)
* [BTRFS storage driver in practice](btrfs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)
+5 -5
View File
@@ -1,14 +1,14 @@
<!--[metadata]>
+++
title = "BTRFS storage in practice"
description = "Learn how to optimize your use of BTRFS driver."
keywords = ["container, storage, driver, BTRFS "]
title = "Btrfs storage in practice"
description = "Learn how to optimize your use of Btrfs driver."
keywords = ["container, storage, driver, Btrfs "]
[menu.main]
parent = "mn_storage_docker"
+++
<![end-metadata]-->
# Docker and BTRFS in practice
# Docker and Btrfs in practice
Btrfs is a next generation copy-on-write filesystem that supports many advanced
storage technologies that make it a good fit for Docker. Btrfs is included in
@@ -250,7 +250,7 @@ Now that you have a Btrfs filesystem mounted at `/var/lib/docker`, the daemon sh
Your Docker host is now configured to use the `btrfs` storage driver.
## BTRFS and Docker performance
## Btrfs and Docker performance
There are several factors that influence Docker's performance under the `btrfs` storage driver.
@@ -307,4 +307,4 @@ One final point, data volumes provide the best and most predictable performance.
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [Select a storage driver](selectadriver.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [BTRFS storage driver in practice](btrfs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
@@ -251,5 +251,5 @@ For detailed information about data volumes [Managing data in containers](https:
* [Select a storage driver](selectadriver.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [BTRFS storage driver in practice](btrfs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)
+2 -2
View File
@@ -18,10 +18,10 @@ Docker relies on driver technology to manage the storage and interactions associ
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [Select a storage driver](selectadriver.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [BTRFS storage driver in practice](btrfs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)
* [OverlayFS in practice](overlayfs-driver.md)
* [FS storage in practice](zfs-driver.md)
* [ZFS storage in practice](zfs-driver.md)
If you are new to Docker containers make sure you read ["Understand images, containers, and storage drivers"](imagesandcontainers.md) first. It explains key concepts and technologies that can help you when working with storage drivers.
@@ -26,7 +26,7 @@ Once you decide which driver is best, you set this driver on the Docker daemon a
|--------------|---------------------|
|OverlayFS |`overlay` |
|AUFS |`aufs` |
|BTRFS |`btrfs` |
|Btrfs |`btrfs` |
|Device Maper |`devicemapper` |
|VFS* |`vfs` |
|ZFS |`zfs` |
@@ -115,5 +115,5 @@ Whichever driver you choose, make sure it has strong community support and momen
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [BTRFS storage driver in practice](btrfs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)
+1 -4
View File
@@ -182,10 +182,7 @@ func (graph *Graph) restore() error {
if graph.driver.Exists(id) {
img, err := graph.loadImage(id)
if err != nil {
if err != io.EOF {
return fmt.Errorf("could not restore image %s: %v", id, err)
}
logrus.Warnf("could not restore image %s due to corrupted files", id)
logrus.Warnf("ignoring image %s, it could not be restored: %v", id, err)
continue
}
graph.imageMutex.Lock(img.Parent)
+91
View File
@@ -3,6 +3,8 @@ package graph
import (
"bufio"
"compress/gzip"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@@ -199,6 +201,11 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
p.layersPushed[dgst] = true
}
// Fix parent chain if necessary
if err = fixHistory(m); err != nil {
return err
}
logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID())
signed, err := manifest.Sign(m, p.trustKey)
if err != nil {
@@ -220,6 +227,90 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
return manSvc.Put(signed)
}
// fixHistory makes sure that the manifest has parent IDs that are consistent
// with its image IDs. Because local image IDs are generated from the
// configuration and filesystem contents, but IDs in the manifest are preserved
// from the original pull, it's possible to have inconsistencies where parent
// IDs don't match up with the other IDs in the manifest. This happens in the
// case where an engine pulls images where are identical except the IDs from the
// manifest - the local ID will be the same, and one of the v1Compatibility
// files gets discarded.
func fixHistory(m *manifest.Manifest) error {
var lastID string
for i := len(m.History) - 1; i >= 0; i-- {
var historyEntry map[string]*json.RawMessage
if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &historyEntry); err != nil {
return err
}
idJSON, present := historyEntry["id"]
if !present || idJSON == nil {
return errors.New("missing id key in v1compatibility file")
}
var id string
if err := json.Unmarshal(*idJSON, &id); err != nil {
return err
}
parentJSON, present := historyEntry["parent"]
if i == len(m.History)-1 {
// The base layer must not reference a parent layer,
// otherwise the manifest is incomplete. There is an
// exception for Windows to handle base layers.
if present && parentJSON != nil {
var parent string
if err := json.Unmarshal(*parentJSON, &parent); err != nil {
return err
}
if parent != "" {
logrus.Debugf("parent id mismatch detected; fixing. parent reference: %s", parent)
delete(historyEntry, "parent")
fixedHistory, err := json.Marshal(historyEntry)
if err != nil {
return err
}
m.History[i].V1Compatibility = string(fixedHistory)
}
}
} else {
// For all other layers, the parent ID should equal the
// ID of the next item in the history list. If it
// doesn't, fix it up (but preserve all other fields,
// possibly including fields that aren't known to this
// engine version).
if !present || parentJSON == nil {
return errors.New("missing parent key in v1compatibility file")
}
var parent string
if err := json.Unmarshal(*parentJSON, &parent); err != nil {
return err
}
if parent != lastID {
logrus.Debugf("parent id mismatch detected; fixing. parent reference: %s actual id: %s", parent, id)
historyEntry["parent"] = rawJSON(lastID)
fixedHistory, err := json.Marshal(historyEntry)
if err != nil {
return err
}
m.History[i].V1Compatibility = string(fixedHistory)
}
}
lastID = id
}
return nil
}
func rawJSON(value interface{}) *json.RawMessage {
jsonval, err := json.Marshal(value)
if err != nil {
return nil
}
return (*json.RawMessage)(&jsonval)
}
func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (digest.Digest, error) {
out := p.config.OutStream
+23
View File
@@ -24,6 +24,9 @@ import (
"github.com/docker/libtrust"
)
// ErrNameIsNotExist returned when there is no image with requested name.
var ErrNameIsNotExist = errors.New("image with specified name does not exist")
// TagStore manages repositories. It encompasses the Graph used for versioned
// storage, as well as various services involved in pushing and pulling
// repositories.
@@ -164,6 +167,26 @@ func (store *TagStore) LookupImage(name string) (*image.Image, error) {
return img, nil
}
// GetID returns ID for image name.
func (store *TagStore) GetID(name string) (string, error) {
repoName, ref := parsers.ParseRepositoryTag(name)
if ref == "" {
ref = tags.DefaultTag
}
store.Lock()
defer store.Unlock()
repoName = registry.NormalizeLocalName(repoName)
repo, ok := store.Repositories[repoName]
if !ok {
return "", ErrNameIsNotExist
}
id, ok := repo[ref]
if !ok {
return "", ErrNameIsNotExist
}
return id, nil
}
// ByID returns a reverse-lookup table of all the names which refer to each
// image - e.g. {"43b5f19b10584": {"base:latest", "base:v1"}}
func (store *TagStore) ByID() map[string][]string {
+62 -2
View File
@@ -80,6 +80,16 @@ check_forked() {
fi
}
rpm_import_repository_key() {
local key=$1; shift
local tmpdir=$(mktemp -d)
chmod 600 "$tmpdir"
gpg --homedir "$tmpdir" --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"
gpg --homedir "$tmpdir" --export --armor "$key" > "$tmpdir"/repo.key
rpm --import "$tmpdir"/repo.key
rm -rf "$tmpdir"
}
do_install() {
case "$(uname -m)" in
*64)
@@ -231,10 +241,60 @@ do_install() {
exit 0
;;
'opensuse project'|opensuse|'suse linux'|sle[sd])
'opensuse project'|opensuse)
echo 'Going to perform the following operations:'
if [ "$repo" != 'main' ]; then
echo ' * add repository obs://Virtualization:containers'
fi
echo ' * install Docker'
$sh_c 'echo "Press CTRL-C to abort"; sleep 3'
if [ "$repo" != 'main' ]; then
# install experimental packages from OBS://Virtualization:containers
(
set -x
zypper -n ar -f obs://Virtualization:containers Virtualization:containers
rpm_import_repository_key 55A0B34D49501BB7CA474F5AA193FBB572174FC2
)
fi
(
set -x
$sh_c 'sleep 3; zypper -n install docker'
zypper -n install docker
)
echo_docker_as_nonroot
exit 0
;;
'suse linux'|sle[sd])
echo 'Going to perform the following operations:'
if [ "$repo" != 'main' ]; then
echo ' * add repository obs://Virtualization:containers'
echo ' * install experimental Docker using packages NOT supported by SUSE'
else
echo ' * add the "Containers" module'
echo ' * install Docker using packages supported by SUSE'
fi
$sh_c 'echo "Press CTRL-C to abort"; sleep 3'
if [ "$repo" != 'main' ]; then
# install experimental packages from OBS://Virtualization:containers
echo >&2 'Warning: installing experimental packages from OBS, these packages are NOT supported by SUSE'
(
set -x
zypper -n ar -f obs://Virtualization:containers/SLE_12 Virtualization:containers
rpm_import_repository_key 55A0B34D49501BB7CA474F5AA193FBB572174FC2
)
else
# Add the containers module
# Note well-1: the SLE machine must already be registered against SUSE Customer Center
# Note well-2: the `-r ""` is required to workaround a known issue of SUSEConnect
(
set -x
SUSEConnect -p sle-module-containers/12/x86_64 -r ""
)
fi
(
set -x
zypper -n install docker
)
echo_docker_as_nonroot
exit 0
+4 -2
View File
@@ -20,7 +20,7 @@ clone git github.com/tchap/go-patricia v2.1.0
clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
#get libnetwork packages
clone git github.com/docker/libnetwork e7719596c01a83f9ef24d33e9d609a64acacd7b8
clone git github.com/docker/libnetwork b4ddf18317b19d6e4bcc821145589749206a7d00
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
@@ -46,7 +46,9 @@ clone git github.com/endophage/gotuf 2df1c8e0a7b7e10ae2113bf37aaa1bf1c1de8cc5
clone git github.com/jfrazelle/go 6e461eb70cb4187b41a84e9a567d7137bdbe0f16
clone git github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
clone git github.com/opencontainers/runc 6c198ae2d065c37f44316e0de3df7f3b88950923 # libcontainer
# this runc commit from branch relabel_fix_docker_1.9.1, pls remove it when you
# update next time
clone git github.com/opencontainers/runc 1349b37bd56f4f5ce2690b5b2c0f53f88a261c67 # libcontainer
# libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json)
clone git github.com/coreos/go-systemd v3
clone git github.com/godbus/dbus v2
+125
View File
@@ -20,6 +20,7 @@ import (
"github.com/docker/docker/builder/dockerfile/command"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/integration/checker"
"github.com/docker/docker/pkg/stringutils"
"github.com/go-check/check"
)
@@ -6223,3 +6224,127 @@ func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) {
_, err := buildImage("test", dockerFile, false)
c.Assert(err, check.NotNil, check.Commentf("image build should have failed"))
}
// #17290
func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) {
testRequires(c, DaemonIsLinux)
name := "testbuildbrokensymlink"
ctx, err := fakeContext(`
FROM busybox
COPY . ./`,
map[string]string{
"foo": "bar",
})
c.Assert(err, checker.IsNil)
defer ctx.Close()
err = os.Symlink(filepath.Join(ctx.Dir, "nosuchfile"), filepath.Join(ctx.Dir, "asymlink"))
c.Assert(err, checker.IsNil)
// warm up cache
_, err = buildImageFromContext(name, ctx, true)
c.Assert(err, checker.IsNil)
// add new file to context, should invalidate cache
err = ioutil.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644)
c.Assert(err, checker.IsNil)
_, out, err := buildImageFromContextWithOut(name, ctx, true)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Not(checker.Contains), "Using cache")
}
func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *check.C) {
testRequires(c, DaemonIsLinux)
name := "testbuildbrokensymlink"
ctx, err := fakeContext(`
FROM busybox
COPY asymlink target`,
map[string]string{
"foo": "bar",
})
c.Assert(err, checker.IsNil)
defer ctx.Close()
err = os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
c.Assert(err, checker.IsNil)
id, err := buildImageFromContext(name, ctx, true)
c.Assert(err, checker.IsNil)
out, _ := dockerCmd(c, "run", "--rm", id, "cat", "target")
c.Assert(out, checker.Matches, "bar")
// change target file should invalidate cache
err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
c.Assert(err, checker.IsNil)
id, out, err = buildImageFromContextWithOut(name, ctx, true)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Not(checker.Contains), "Using cache")
out, _ = dockerCmd(c, "run", "--rm", id, "cat", "target")
c.Assert(out, checker.Matches, "baz")
}
func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *check.C) {
testRequires(c, DaemonIsLinux)
name := "testbuildbrokensymlink"
ctx, err := fakeContext(`
FROM busybox
COPY asymlink /`,
map[string]string{
"foo/abc": "bar",
"foo/def": "baz",
})
c.Assert(err, checker.IsNil)
defer ctx.Close()
err = os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
c.Assert(err, checker.IsNil)
id, err := buildImageFromContext(name, ctx, true)
c.Assert(err, checker.IsNil)
out, _ := dockerCmd(c, "run", "--rm", id, "cat", "abc", "def")
c.Assert(out, checker.Matches, "barbaz")
// change target file should invalidate cache
err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644)
c.Assert(err, checker.IsNil)
id, out, err = buildImageFromContextWithOut(name, ctx, true)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Not(checker.Contains), "Using cache")
out, _ = dockerCmd(c, "run", "--rm", id, "cat", "abc", "def")
c.Assert(out, checker.Matches, "barbax")
}
// TestBuildSymlinkBasename tests that target file gets basename from symlink,
// not from the target file.
func (s *DockerSuite) TestBuildSymlinkBasename(c *check.C) {
testRequires(c, DaemonIsLinux)
name := "testbuildbrokensymlink"
ctx, err := fakeContext(`
FROM busybox
COPY asymlink /`,
map[string]string{
"foo": "bar",
})
c.Assert(err, checker.IsNil)
defer ctx.Close()
err = os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
c.Assert(err, checker.IsNil)
id, err := buildImageFromContext(name, ctx, true)
c.Assert(err, checker.IsNil)
out, _ := dockerCmd(c, "run", "--rm", id, "cat", "asymlink")
c.Assert(out, checker.Matches, "bar")
}
+53 -4
View File
@@ -377,6 +377,29 @@ func (s *DockerSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
}
}
// TestDaemonIPv6FixedCIDRAndMac checks that when the daemon is started with ipv6 fixed CIDR
// the running containers are given a an IPv6 address derived from the MAC address and the ipv6 fixed CIDR
func (s *DockerSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) {
err := setupV6()
c.Assert(err, checker.IsNil)
d := NewDaemon(c)
err = d.StartWithBusybox("--ipv6", "--fixed-cidr-v6='2001:db8:1::/64'")
c.Assert(err, checker.IsNil)
defer d.Stop()
out, err := d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox")
c.Assert(err, checker.IsNil)
out, err = d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}'", "ipv6test")
c.Assert(err, checker.IsNil)
c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff")
err = teardownV6()
c.Assert(err, checker.IsNil)
}
func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
}
@@ -794,7 +817,33 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) {
}
}
func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidrFixedCIDREqualBridgeNetwork(c *check.C) {
func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr2(c *check.C) {
d := s.d
bridgeName := "external-bridge"
bridgeIP := "10.2.2.1/16"
out, err := createInterface(c, "bridge", bridgeName, bridgeIP)
c.Assert(err, check.IsNil, check.Commentf(out))
defer deleteInterface(c, bridgeName)
err = d.StartWithBusybox("--bip", bridgeIP, "--fixed-cidr", "10.2.2.0/24")
c.Assert(err, check.IsNil)
defer s.d.Restart()
out, err = d.Cmd("run", "-d", "--name", "bb", "busybox", "top")
c.Assert(err, checker.IsNil, check.Commentf(out))
defer d.Cmd("stop", "bb")
out, err = d.Cmd("exec", "bb", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
c.Assert(out, checker.Equals, "10.2.2.0\n")
out, err = d.Cmd("run", "--rm", "busybox", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
c.Assert(err, checker.IsNil, check.Commentf(out))
c.Assert(out, checker.Equals, "10.2.2.2\n")
}
func (s *DockerDaemonSuite) TestDaemonBridgeFixedCIDREqualBridgeNetwork(c *check.C) {
d := s.d
bridgeName := "external-bridge"
@@ -1242,10 +1291,10 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) {
}
id := strings.TrimSpace(out)
out, err = s.d.Cmd("logs", id)
if err != nil {
c.Fatalf("Logs request should be sent and then fail with \"none\" driver")
if err == nil {
c.Fatalf("Logs should fail with 'none' driver")
}
if !strings.Contains(out, `Error running logs job: Failed to get logging factory: logger: no log driver named 'none' is registered`) {
if !strings.Contains(out, `"logs" command is supported only for "json-file" and "journald" logging drivers (got: none)`) {
c.Fatalf("There should be an error about none not being a recognized log driver, got: %s", out)
}
}
@@ -389,3 +389,11 @@ func (s *DockerSuite) TestInspectTempateError(c *check.C) {
c.Assert(err, check.Not(check.IsNil))
c.Assert(out, checker.Contains, "Template parsing error")
}
func (s *DockerSuite) TestInspectJSONFields(c *check.C) {
dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top")
out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='{{.HostConfig.Dns}}'", "busybox")
c.Assert(err, check.IsNil)
c.Assert(out, checker.Equals, "[]\n")
}
@@ -15,6 +15,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions/v1p20"
"github.com/docker/docker/pkg/integration/checker"
"github.com/docker/docker/runconfig"
"github.com/docker/libnetwork/driverapi"
remoteapi "github.com/docker/libnetwork/drivers/remote/api"
"github.com/docker/libnetwork/ipamapi"
@@ -516,6 +517,7 @@ func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *
func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
hostsFile := "/etc/hosts"
cstmBridgeNw := "custom-bridge-nw"
cstmBridgeNw1 := "custom-bridge-nw1"
dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw)
assertNwIsAvailable(c, cstmBridgeNw)
@@ -539,6 +541,18 @@ func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
c.Assert(string(hosts1), checker.Equals, string(hosts1post),
check.Commentf("Unexpected %s change on anonymous container creation", hostsFile))
// Connect the 2nd container to a new network and verify the
// first container /etc/hosts file still hasn't changed.
dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw1)
assertNwIsAvailable(c, cstmBridgeNw1)
dockerCmd(c, "network", "connect", cstmBridgeNw1, cid2)
hosts1post, err = readContainerFileWithExec(cid1, hostsFile)
c.Assert(err, checker.IsNil)
c.Assert(string(hosts1), checker.Equals, string(hosts1post),
check.Commentf("Unexpected %s change on container connect", hostsFile))
// start a named container
cName := "AnyName"
out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "--name", cName, "busybox", "top")
@@ -725,3 +739,46 @@ func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRe
verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
}
func (s *DockerNetworkSuite) TestDockerNetworkRunNetByID(c *check.C) {
out, _ := dockerCmd(c, "network", "create", "one")
dockerCmd(c, "run", "-d", "--net", strings.TrimSpace(out), "busybox", "top")
}
func (s *DockerNetworkSuite) TestDockerNetworkConnectToHostFromOtherNetwork(c *check.C) {
dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
c.Assert(waitRun("container1"), check.IsNil)
dockerCmd(c, "network", "disconnect", "bridge", "container1")
out, _, err := dockerCmdWithError("network", "connect", "host", "container1")
c.Assert(err, checker.NotNil, check.Commentf(out))
c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetwork.Error())
}
func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromHost(c *check.C) {
dockerCmd(c, "run", "-d", "--name", "container1", "--net=host", "busybox", "top")
c.Assert(waitRun("container1"), check.IsNil)
out, _, err := dockerCmdWithError("network", "disconnect", "host", "container1")
c.Assert(err, checker.NotNil, check.Commentf("Should err out disconnect from host"))
c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetwork.Error())
}
func (s *DockerNetworkSuite) TestDockerNetworkConnectWithPortMapping(c *check.C) {
dockerCmd(c, "network", "create", "test1")
dockerCmd(c, "run", "-d", "--name", "c1", "-p", "5000:5000", "busybox", "top")
c.Assert(waitRun("c1"), check.IsNil)
dockerCmd(c, "network", "connect", "test1", "c1")
}
func (s *DockerNetworkSuite) TestDockerNetworkConnectWithMac(c *check.C) {
macAddress := "02:42:ac:11:00:02"
dockerCmd(c, "network", "create", "mynetwork")
dockerCmd(c, "run", "--name=test", "-d", "--mac-address", macAddress, "busybox", "top")
c.Assert(waitRun("test"), check.IsNil)
mac1, err := inspectField("test", "NetworkSettings.Networks.bridge.MacAddress")
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(mac1), checker.Equals, macAddress)
dockerCmd(c, "network", "connect", "mynetwork", "test")
mac2, err := inspectField("test", "NetworkSettings.Networks.mynetwork.MacAddress")
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(mac2), checker.Not(checker.Equals), strings.TrimSpace(mac1))
}
+43
View File
@@ -2,13 +2,16 @@ package main
import (
"archive/tar"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/docker/docker/image"
"github.com/go-check/check"
)
@@ -97,6 +100,46 @@ func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
}
}
// TestPushBadParentChain tries to push an image with a corrupted parent chain
// in the v1compatibility files, and makes sure the push process fixes it.
func (s *DockerRegistrySuite) TestPushBadParentChain(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/badparent", privateRegistryURL)
id, err := buildImage(repoName, `
FROM busybox
CMD echo "adding another layer"
`, true)
if err != nil {
c.Fatal(err)
}
// Push to create v1compatibility file
dockerCmd(c, "push", repoName)
// Corrupt the parent in the v1compatibility file from the top layer
filename := filepath.Join(dockerBasePath, "graph", id, "v1Compatibility")
jsonBytes, err := ioutil.ReadFile(filename)
c.Assert(err, check.IsNil, check.Commentf("Could not read v1Compatibility file: %s", err))
var img image.Image
err = json.Unmarshal(jsonBytes, &img)
c.Assert(err, check.IsNil, check.Commentf("Could not unmarshal json: %s", err))
img.Parent = "1234123412341234123412341234123412341234123412341234123412341234"
jsonBytes, err = json.Marshal(&img)
c.Assert(err, check.IsNil, check.Commentf("Could not marshal json: %s", err))
err = ioutil.WriteFile(filename, jsonBytes, 0600)
c.Assert(err, check.IsNil, check.Commentf("Could not write v1Compatibility file: %s", err))
dockerCmd(c, "push", repoName)
// pull should succeed
dockerCmd(c, "pull", repoName)
}
func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
emptyTarball, err := ioutil.TempFile("", "empty_tarball")
+14 -2
View File
@@ -1214,6 +1214,14 @@ func buildImage(name, dockerfile string, useCache bool, buildFlags ...string) (s
}
func buildImageFromContext(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, error) {
id, _, err := buildImageFromContextWithOut(name, ctx, useCache, buildFlags...)
if err != nil {
return "", err
}
return id, nil
}
func buildImageFromContextWithOut(name string, ctx *FakeContext, useCache bool, buildFlags ...string) (string, string, error) {
args := []string{"build", "-t", name}
if !useCache {
args = append(args, "--no-cache")
@@ -1224,9 +1232,13 @@ func buildImageFromContext(name string, ctx *FakeContext, useCache bool, buildFl
buildCmd.Dir = ctx.Dir
out, exitCode, err := runCommandWithOutput(buildCmd)
if err != nil || exitCode != 0 {
return "", fmt.Errorf("failed to build the image: %s", out)
return "", "", fmt.Errorf("failed to build the image: %s", out)
}
return getIDByName(name)
id, err := getIDByName(name)
if err != nil {
return "", "", err
}
return id, out, nil
}
func buildImageFromPath(name, path string, useCache bool, buildFlags ...string) (string, error) {
+10
View File
@@ -101,6 +101,16 @@ func (opts *ListOpts) GetAll() []string {
return (*opts.values)
}
// GetAllOrEmpty returns the values of the slice
// or an empty slice when there are no values.
func (opts *ListOpts) GetAllOrEmpty() []string {
v := *opts.values
if v == nil {
return make([]string, 0)
}
return v
}
// Get checks the existence of the specified key.
func (opts *ListOpts) Get(key string) bool {
for _, k := range *opts.values {
+21 -6
View File
@@ -25,6 +25,7 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
defer pools.BufioReader32KPool.Put(trBuf)
var dirs []*tar.Header
unpackedPaths := make(map[string]struct{})
if options == nil {
options = &TarOptions{}
@@ -134,14 +135,27 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
if strings.HasPrefix(base, WhiteoutPrefix) {
dir := filepath.Dir(path)
if base == WhiteoutOpaqueDir {
fi, err := os.Lstat(dir)
if err != nil && !os.IsNotExist(err) {
_, err := os.Lstat(dir)
if err != nil {
return 0, err
}
if err := os.RemoveAll(dir); err != nil {
return 0, err
}
if err := os.Mkdir(dir, fi.Mode()&os.ModePerm); err != nil {
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
if os.IsNotExist(err) {
err = nil // parent was deleted
}
return err
}
if path == dir {
return nil
}
if _, exists := unpackedPaths[path]; !exists {
err := os.RemoveAll(path)
return err
}
return nil
})
if err != nil {
return 0, err
}
} else {
@@ -214,6 +228,7 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
if hdr.Typeflag == tar.TypeDir {
dirs = append(dirs, hdr)
}
unpackedPaths[path] = struct{}{}
}
}
+180
View File
@@ -2,7 +2,14 @@ package archive
import (
"archive/tar"
"io"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/docker/docker/pkg/ioutils"
)
func TestApplyLayerInvalidFilenames(t *testing.T) {
@@ -188,3 +195,176 @@ func TestApplyLayerInvalidSymlink(t *testing.T) {
}
}
}
func TestApplyLayerWhiteouts(t *testing.T) {
wd, err := ioutil.TempDir("", "graphdriver-test-whiteouts")
if err != nil {
return
}
defer os.RemoveAll(wd)
base := []string{
".baz",
"bar/",
"bar/bax",
"bar/bay/",
"baz",
"foo/",
"foo/.abc",
"foo/.bcd/",
"foo/.bcd/a",
"foo/cde/",
"foo/cde/def",
"foo/cde/efg",
"foo/fgh",
"foobar",
}
type tcase struct {
change, expected []string
}
tcases := []tcase{
{
base,
base,
},
{
[]string{
".bay",
".wh.baz",
"foo/",
"foo/.bce",
"foo/.wh..wh..opq",
"foo/cde/",
"foo/cde/efg",
},
[]string{
".bay",
".baz",
"bar/",
"bar/bax",
"bar/bay/",
"foo/",
"foo/.bce",
"foo/cde/",
"foo/cde/efg",
"foobar",
},
},
{
[]string{
".bay",
".wh..baz",
".wh.foobar",
"foo/",
"foo/.abc",
"foo/.wh.cde",
"bar/",
},
[]string{
".bay",
"bar/",
"bar/bax",
"bar/bay/",
"foo/",
"foo/.abc",
"foo/.bce",
},
},
{
[]string{
".abc",
".wh..wh..opq",
"foobar",
},
[]string{
".abc",
"foobar",
},
},
}
for i, tc := range tcases {
l, err := makeTestLayer(tc.change)
if err != nil {
t.Fatal(err)
}
_, err = UnpackLayer(wd, l, nil)
if err != nil {
t.Fatal(err)
}
err = l.Close()
if err != nil {
t.Fatal(err)
}
paths, err := readDirContents(wd)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(tc.expected, paths) {
t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths)
}
}
}
func makeTestLayer(paths []string) (rc io.ReadCloser, err error) {
tmpDir, err := ioutil.TempDir("", "graphdriver-test-mklayer")
if err != nil {
return
}
defer func() {
if err != nil {
os.RemoveAll(tmpDir)
}
}()
for _, p := range paths {
if p[len(p)-1] == filepath.Separator {
if err = os.MkdirAll(filepath.Join(tmpDir, p), 0700); err != nil {
return
}
} else {
if err = ioutil.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil {
return
}
}
}
archive, err := Tar(tmpDir, Uncompressed)
if err != nil {
return
}
return ioutils.NewReadCloserWrapper(archive, func() error {
err := archive.Close()
os.RemoveAll(tmpDir)
return err
}), nil
}
func readDirContents(root string) ([]string, error) {
var files []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == root {
return nil
}
rel, err := filepath.Rel(root, path)
if err != nil {
return err
}
if info.IsDir() {
rel = rel + "/"
}
files = append(files, rel)
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
+51 -10
View File
@@ -1,32 +1,54 @@
package ioutils
import (
"errors"
"io"
"net/http"
"sync"
)
// WriteFlusher wraps the Write and Flush operation.
// WriteFlusher wraps the Write and Flush operation ensuring that every write
// is a flush. In addition, the Close method can be called to intercept
// Read/Write calls if the targets lifecycle has already ended.
type WriteFlusher struct {
sync.Mutex
mu sync.Mutex
w io.Writer
flusher http.Flusher
flushed bool
closed error
// TODO(stevvooe): Use channel for closed instead, remove mutex. Using a
// channel will allow one to properly order the operations.
}
var errWriteFlusherClosed = errors.New("writeflusher: closed")
func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
wf.Lock()
defer wf.Unlock()
wf.mu.Lock()
defer wf.mu.Unlock()
if wf.closed != nil {
return 0, wf.closed
}
n, err = wf.w.Write(b)
wf.flushed = true
wf.flusher.Flush()
wf.flush() // every write is a flush.
return n, err
}
// Flush the stream immediately.
func (wf *WriteFlusher) Flush() {
wf.Lock()
defer wf.Unlock()
wf.mu.Lock()
defer wf.mu.Unlock()
wf.flush()
}
// flush the stream immediately without taking a lock. Used internally.
func (wf *WriteFlusher) flush() {
if wf.closed != nil {
return
}
wf.flushed = true
wf.flusher.Flush()
}
@@ -34,11 +56,30 @@ func (wf *WriteFlusher) Flush() {
// Flushed returns the state of flushed.
// If it's flushed, return true, or else it return false.
func (wf *WriteFlusher) Flushed() bool {
wf.Lock()
defer wf.Unlock()
// BUG(stevvooe): Remove this method. Its use is inherently racy. Seems to
// be used to detect whether or a response code has been issued or not.
// Another hook should be used instead.
wf.mu.Lock()
defer wf.mu.Unlock()
return wf.flushed
}
// Close closes the write flusher, disallowing any further writes to the
// target. After the flusher is closed, all calls to write or flush will
// result in an error.
func (wf *WriteFlusher) Close() error {
wf.mu.Lock()
defer wf.mu.Unlock()
if wf.closed != nil {
return wf.closed
}
wf.closed = errWriteFlusherClosed
return nil
}
// NewWriteFlusher returns a new WriteFlusher.
func NewWriteFlusher(w io.Writer) *WriteFlusher {
var flusher http.Flusher
@@ -6,6 +6,7 @@ import (
"bytes"
"errors"
"io/ioutil"
"os"
)
var (
@@ -14,13 +15,23 @@ var (
// file to check to determine Operating System
etcOsRelease = "/etc/os-release"
// used by stateless systems like Clear Linux
altEtcOSRelease = "/usr/lib/os-release"
)
// GetOperatingSystem gets the name of the current operating system.
func GetOperatingSystem() (string, error) {
b, err := ioutil.ReadFile(etcOsRelease)
if err != nil {
return "", err
if _, err2 := os.Stat(altEtcOSRelease); err2 == nil {
b, err2 = ioutil.ReadFile(altEtcOSRelease)
if err2 != nil {
return "", err2
}
} else {
return "", err
}
}
if i := bytes.Index(b, []byte("PRETTY_NAME")); i >= 0 {
b = b[i+13:]
+2 -1
View File
@@ -266,7 +266,8 @@ installed and available at runtime:
* iptables version 1.4 or later
* procps (or similar provider of a "ps" executable)
* e2fsprogs version 1.4.12 or later (in use: mkfs.ext4, mkfs.xfs, tune2fs)
* e2fsprogs version 1.4.12 or later (in use: mkfs.ext4, tune2fs)
* xfsprogs (in use: mkfs.xfs)
* XZ Utils version 4.9 or later
* a [properly
mounted](https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount)
+53 -24
View File
@@ -239,15 +239,28 @@ func validateNoSchema(reposName string) error {
// ValidateRepositoryName validates a repository name
func ValidateRepositoryName(reposName string) error {
var err error
if err = validateNoSchema(reposName); err != nil {
return err
_, _, err := loadRepositoryName(reposName, true)
return err
}
// loadRepositoryName returns the repo name splitted into index name
// and remote repo name. It returns an error if the name is not valid.
func loadRepositoryName(reposName string, checkRemoteName bool) (string, string, error) {
if err := validateNoSchema(reposName); err != nil {
return "", "", err
}
indexName, remoteName := splitReposName(reposName)
if _, err = ValidateIndexName(indexName); err != nil {
return err
var err error
if indexName, err = ValidateIndexName(indexName); err != nil {
return "", "", err
}
return validateRemoteName(remoteName)
if checkRemoteName {
if err = validateRemoteName(remoteName); err != nil {
return "", "", err
}
}
return indexName, remoteName, nil
}
// NewIndexInfo returns IndexInfo configuration from indexName
@@ -301,34 +314,22 @@ func splitReposName(reposName string) (string, string) {
// NewRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool) (*RepositoryInfo, error) {
if err := validateNoSchema(reposName); err != nil {
indexName, remoteName, err := loadRepositoryName(reposName, !bySearch)
if err != nil {
return nil, err
}
indexName, remoteName := splitReposName(reposName)
if !bySearch {
if err := validateRemoteName(remoteName); err != nil {
return nil, err
}
}
repoInfo := &RepositoryInfo{
RemoteName: remoteName,
}
var err error
repoInfo.Index, err = config.NewIndexInfo(indexName)
if err != nil {
return nil, err
}
if repoInfo.Index.Official {
normalizedName := repoInfo.RemoteName
if strings.HasPrefix(normalizedName, "library/") {
// If pull "library/foo", it's stored locally under "foo"
normalizedName = strings.SplitN(normalizedName, "/", 2)[1]
}
normalizedName := normalizeLibraryRepoName(repoInfo.RemoteName)
repoInfo.LocalName = normalizedName
repoInfo.RemoteName = normalizedName
@@ -342,7 +343,7 @@ func (config *ServiceConfig) NewRepositoryInfo(reposName string, bySearch bool)
repoInfo.CanonicalName = "docker.io/" + repoInfo.RemoteName
} else {
repoInfo.LocalName = repoInfo.Index.Name + "/" + repoInfo.RemoteName
repoInfo.LocalName = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
repoInfo.CanonicalName = repoInfo.LocalName
}
@@ -378,10 +379,38 @@ func ParseIndexInfo(reposName string) (*IndexInfo, error) {
// NormalizeLocalName transforms a repository name into a normalize LocalName
// Passes through the name without transformation on error (image id, etc)
// It does not use the repository info because we don't want to load
// the repository index and do request over the network.
func NormalizeLocalName(name string) string {
repoInfo, err := ParseRepositoryInfo(name)
indexName, remoteName, err := loadRepositoryName(name, true)
if err != nil {
return name
}
return repoInfo.LocalName
var officialIndex bool
// Return any configured index info, first.
if index, ok := emptyServiceConfig.IndexConfigs[indexName]; ok {
officialIndex = index.Official
}
if officialIndex {
return normalizeLibraryRepoName(remoteName)
}
return localNameFromRemote(indexName, remoteName)
}
// normalizeLibraryRepoName removes the library prefix from
// the repository name for official repos.
func normalizeLibraryRepoName(name string) string {
if strings.HasPrefix(name, "library/") {
// If pull "library/foo", it's stored locally under "foo"
name = strings.SplitN(name, "/", 2)[1]
}
return name
}
// localNameFromRemote combines the index name and the repo remote name
// to generate a repo local name.
func localNameFromRemote(indexName, remoteName string) string {
return indexName + "/" + remoteName
}
+27 -20
View File
@@ -21,6 +21,8 @@ var (
ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: --net=<NETWORK> can't be used with links. This would result in undefined behavior")
// ErrConflictSharedNetwork conflict between private and other networks
ErrConflictSharedNetwork = fmt.Errorf("Container sharing network namespace with another container or host cannot be connected to any other network")
// ErrConflictHostNetwork conflict from being disconnected from host network or connected to host network.
ErrConflictHostNetwork = fmt.Errorf("Container cannot be disconnected from host network or connected to host network")
// ErrConflictNoNetwork conflict between private and other networks
ErrConflictNoNetwork = fmt.Errorf("Container cannot be connected to multiple networks with one of the networks in --none mode")
// ErrConflictNetworkAndDNS conflict between --dns and the network mode
@@ -361,26 +363,31 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
PortBindings: portBindings,
Links: flLinks.GetAll(),
PublishAllPorts: *flPublishAll,
DNS: flDNS.GetAll(),
DNSSearch: flDNSSearch.GetAll(),
DNSOptions: flDNSOptions.GetAll(),
ExtraHosts: flExtraHosts.GetAll(),
VolumesFrom: flVolumesFrom.GetAll(),
NetworkMode: NetworkMode(*flNetMode),
IpcMode: ipcMode,
PidMode: pidMode,
UTSMode: utsMode,
Devices: deviceMappings,
CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...),
CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...),
GroupAdd: flGroupAdd.GetAll(),
RestartPolicy: restartPolicy,
SecurityOpt: flSecurityOpt.GetAll(),
ReadonlyRootfs: *flReadonlyRootfs,
Ulimits: flUlimits.GetList(),
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
CgroupParent: *flCgroupParent,
VolumeDriver: *flVolumeDriver,
// Make sure the dns fields are never nil.
// New containers don't ever have those fields nil,
// but pre created containers can still have those nil values.
// See https://github.com/docker/docker/pull/17779
// for a more detailed explanation on why we don't want that.
DNS: flDNS.GetAllOrEmpty(),
DNSSearch: flDNSSearch.GetAllOrEmpty(),
DNSOptions: flDNSOptions.GetAllOrEmpty(),
ExtraHosts: flExtraHosts.GetAll(),
VolumesFrom: flVolumesFrom.GetAll(),
NetworkMode: NetworkMode(*flNetMode),
IpcMode: ipcMode,
PidMode: pidMode,
UTSMode: utsMode,
Devices: deviceMappings,
CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...),
CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...),
GroupAdd: flGroupAdd.GetAll(),
RestartPolicy: restartPolicy,
SecurityOpt: flSecurityOpt.GetAll(),
ReadonlyRootfs: *flReadonlyRootfs,
Ulimits: flUlimits.GetList(),
LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts},
CgroupParent: *flCgroupParent,
VolumeDriver: *flVolumeDriver,
}
// When allocating stdin in attached mode, close stdin at client disconnect
+1 -4
View File
@@ -128,14 +128,11 @@ type ipamData struct {
type driverTable map[string]*driverData
//type networkTable map[string]*network
//type endpointTable map[string]*endpoint
type ipamTable map[string]*ipamData
type sandboxTable map[string]*sandbox
type controller struct {
id string
//networks networkTable
id string
drivers driverTable
ipamDrivers ipamTable
sandboxes sandboxTable
@@ -98,6 +98,7 @@ type bridgeNetwork struct {
endpoints map[string]*bridgeEndpoint // key: endpoint id
portMapper *portmapper.PortMapper
driver *driver // The network's driver
veth *netlink.Veth
sync.Mutex
}
@@ -450,6 +451,8 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
}
if len(ipamV6Data) > 0 {
c.AddressIPv6 = ipamV6Data[0].Pool
if ipamV6Data[0].Gateway != nil {
c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
}
@@ -738,7 +741,9 @@ func (d *driver) DeleteNetwork(nid string) error {
// We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior.
if !config.DefaultBridge {
err = netlink.LinkDel(n.bridge.Link)
if err := netlink.LinkDel(n.bridge.Link); err != nil {
logrus.Warnf("Failed to remove bridge interface %s on network %s delete: %v", config.BridgeName, nid, err)
}
}
return d.storeDelete(config)
@@ -870,11 +875,13 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
return err
}
logrus.Warnf("network %v", n)
logrus.Warnf("veth %v", n.veth)
// Generate and add the interface pipe host <-> sandbox
veth := &netlink.Veth{
n.veth = &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0},
PeerName: containerIfName}
if err = netlink.LinkAdd(veth); err != nil {
if err = netlink.LinkAdd(n.veth); err != nil {
return types.InternalErrorf("failed to add the host (%s) <=> sandbox (%s) pair interfaces: %v", hostIfName, containerIfName, err)
}
@@ -959,13 +966,20 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
if endpoint.addrv6 == nil && config.EnableIPv6 {
var ip6 net.IP
network := n.bridge.bridgeIPv6
if config.AddressIPv6 != nil {
network = config.AddressIPv6
}
ones, _ := network.Mask.Size()
if ones <= 80 {
ip6 = make(net.IP, len(network.IP))
copy(ip6, network.IP)
for i, h := range endpoint.macAddress {
ip6[i+10] = h
}
if ones > 80 {
err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
return err
}
ip6 = make(net.IP, len(network.IP))
copy(ip6, network.IP)
for i, h := range endpoint.macAddress {
ip6[i+10] = h
}
endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
@@ -1037,9 +1051,8 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
// Remove port mappings. Do not stop endpoint delete on unmap failure
n.releasePorts(ep)
// Try removal of link. Discard error: link pair might have
// already been deleted by sandbox delete. Make sure defer
// does not see this error either.
// Try removal of link. Discard error: it is a best effort.
// Also make sure defer does not see this error either.
if link, err := netlink.LinkByName(ep.srcName); err == nil {
netlink.LinkDel(link)
}
@@ -1100,6 +1113,11 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro
m[netlabel.MacAddress] = ep.macAddress
}
// Add details of the bridge
m[netlabel.BridgeName] = n.config.BridgeName
m[netlabel.BridgeLinkName] = n.veth.LinkAttrs.Name
m[netlabel.BridgeEID] = eid
return m, nil
}
+19 -7
View File
@@ -584,7 +584,8 @@ func (ep *endpoint) Delete() error {
ep.Lock()
epid := ep.id
name := ep.name
if ep.sandboxID != "" {
sb, _ := n.getController().SandboxByID(ep.sandboxID)
if sb != nil {
ep.Unlock()
return &ActiveContainerError{name: name, id: epid}
}
@@ -737,7 +738,7 @@ func (ep *endpoint) DataScope() string {
return ep.getNetwork().DataScope()
}
func (ep *endpoint) assignAddress() error {
func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
var (
ipam ipamapi.Ipam
err error
@@ -754,11 +755,18 @@ func (ep *endpoint) assignAddress() error {
if err != nil {
return err
}
err = ep.assignAddressVersion(4, ipam)
if err != nil {
return err
if assignIPv4 {
if err = ep.assignAddressVersion(4, ipam); err != nil {
return err
}
}
return ep.assignAddressVersion(6, ipam)
if assignIPv6 {
err = ep.assignAddressVersion(6, ipam)
}
return err
}
func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
@@ -787,7 +795,11 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
}
for _, d := range ipInfo {
addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
var prefIP net.IP
if *address != nil {
prefIP = (*address).IP
}
addr, _, err := ipam.RequestAddress(d.PoolID, prefIP, nil)
if err == nil {
ep.Lock()
*address = addr
+1 -5
View File
@@ -220,7 +220,7 @@ func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool
return nil, nil, nil, ipamapi.ErrInvalidPool
}
if subPool != "" {
if ipr, err = getAddressRange(subPool); err != nil {
if ipr, err = getAddressRange(subPool, nw); err != nil {
return nil, nil, nil, err
}
}
@@ -431,9 +431,6 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
aSpace.Unlock()
mask := p.Pool.Mask
if p.Range != nil {
mask = p.Range.Sub.Mask
}
h, err := types.GetHostPartIP(address, mask)
if err != nil {
@@ -471,7 +468,6 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres
ordinal = ipToUint64(types.GetMinimalIP(hostPart))
err = bitmask.Set(ordinal)
} else {
base.IP = ipr.Sub.IP
ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End)
}
if err != nil {
+3 -3
View File
@@ -15,12 +15,12 @@ const (
v6 = 6
)
func getAddressRange(pool string) (*AddressRange, error) {
func getAddressRange(pool string, masterNw *net.IPNet) (*AddressRange, error) {
ip, nw, err := net.ParseCIDR(pool)
if err != nil {
return nil, ipamapi.ErrInvalidSubPool
}
lIP, e := types.GetHostPartIP(nw.IP, nw.Mask)
lIP, e := types.GetHostPartIP(nw.IP, masterNw.Mask)
if e != nil {
return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
}
@@ -28,7 +28,7 @@ func getAddressRange(pool string) (*AddressRange, error) {
if e != nil {
return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
}
hIP, e := types.GetHostPartIP(bIP, nw.Mask)
hIP, e := types.GetHostPartIP(bIP, masterNw.Mask)
if e != nil {
return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
}
@@ -33,6 +33,22 @@ const (
// DriverMTU constant represents the MTU size for the network driver
DriverMTU = DriverPrefix + ".mtu"
// BridgeName constant represents the name of the network bridge
BridgeName = "io.docker.network.bridge.name"
// BridgeEID is the endpoint ID for the bridge
BridgeEID = "io.docker.network.bridge.endpoint_id"
// BridgeLinkName constant represents the interface name created on
// the host side.
BridgeLinkName = "io.docker.network.bridge.linkname"
// KVProvider constant represents the KV provider backend
KVProvider = DriverPrefix + ".kv_provider"
// KVProviderURL constant represents the KV provider URL
KVProviderURL = DriverPrefix + ".kv_provider_url"
// OverlayBindInterface constant represents overlay driver bind interface
OverlayBindInterface = DriverPrefix + ".overlay.bind_interface"
+21 -1
View File
@@ -152,6 +152,7 @@ type network struct {
ipamV4Info []*IpamInfo
ipamV6Info []*IpamInfo
enableIPv6 bool
postIPv6 bool
epCnt *endpointCnt
generic options.Generic
dbIndex uint64
@@ -298,6 +299,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
dstN.ipamType = n.ipamType
dstN.enableIPv6 = n.enableIPv6
dstN.persist = n.persist
dstN.postIPv6 = n.postIPv6
dstN.dbIndex = n.dbIndex
dstN.dbExists = n.dbExists
dstN.drvOnce = n.drvOnce
@@ -358,6 +360,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
netMap["generic"] = n.generic
}
netMap["persist"] = n.persist
netMap["postIPv6"] = n.postIPv6
if len(n.ipamV4Config) > 0 {
ics, err := json.Marshal(n.ipamV4Config)
if err != nil {
@@ -418,6 +421,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
if v, ok := netMap["persist"]; ok {
n.persist = v.(bool)
}
if v, ok := netMap["postIPv6"]; ok {
n.postIPv6 = v.(bool)
}
if v, ok := netMap["ipamType"]; ok {
n.ipamType = v.(string)
} else {
@@ -505,6 +511,16 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
}
}
// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
// to a container as combination of fixed-cidr-v6 + mac-address
// TODO: Remove this option setter once we support endpoint ipam options
func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
return func(n *network) {
n.postIPv6 = enable
}
}
func (n *network) processOptions(options ...NetworkOption) {
for _, opt := range options {
if opt != nil {
@@ -655,7 +671,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
ep.processOptions(options...)
if err = ep.assignAddress(); err != nil {
if err = ep.assignAddress(true, !n.postIPv6); err != nil {
return nil, err
}
defer func() {
@@ -675,6 +691,10 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
}
}()
if err = ep.assignAddress(false, n.postIPv6); err != nil {
return nil, err
}
if err = n.getController().updateToStore(ep); err != nil {
return nil, err
}
+25 -9
View File
@@ -177,13 +177,18 @@ func (sb *sandbox) Delete() error {
continue
}
if err := ep.Leave(sb); err != nil {
// Retain the sanbdox if we can't obtain the network from store.
if _, err := c.getNetworkFromStore(ep.getNetwork().ID()); err != nil {
retain = true
log.Warnf("Failed getting network for ep %s during sandbox %s delete: %v", ep.ID(), sb.ID(), err)
continue
}
if err := ep.Leave(sb); err != nil {
log.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err)
}
if err := ep.Delete(); err != nil {
retain = true
log.Warnf("Failed deleting endpoint %s: %v\n", ep.ID(), err)
}
}
@@ -455,7 +460,7 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
i := ep.iface
ep.Unlock()
if i.srcName != "" {
if i != nil && i.srcName != "" {
var ifaceOptions []osl.IfaceOption
ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes))
@@ -951,6 +956,11 @@ func OptionGeneric(generic map[string]interface{}) SandboxOption {
func (eh epHeap) Len() int { return len(eh) }
func (eh epHeap) Less(i, j int) bool {
var (
cip, cjp int
ok bool
)
ci, _ := eh[i].getSandbox()
cj, _ := eh[j].getSandbox()
@@ -965,14 +975,20 @@ func (eh epHeap) Less(i, j int) bool {
return true
}
cip, ok := ci.epPriority[eh[i].ID()]
if !ok {
cip = 0
if ci != nil {
cip, ok = ci.epPriority[eh[i].ID()]
if !ok {
cip = 0
}
}
cjp, ok := cj.epPriority[eh[j].ID()]
if !ok {
cjp = 0
if cj != nil {
cjp, ok = cj.epPriority[eh[j].ID()]
if !ok {
cjp = 0
}
}
if cip == cjp {
return eh[i].network.Name() < eh[j].network.Name()
}
-1
View File
@@ -176,7 +176,6 @@ func (n *network) getEndpointsFromStore() ([]*endpoint, error) {
for _, kvo := range kvol {
ep := kvo.(*endpoint)
ep.network = n
epl = append(epl, ep)
}
}
@@ -65,6 +65,11 @@ func Validate(label string) error {
return nil
}
// RelabelNeeded checks whether the user requested a relabel
func RelabelNeeded(label string) bool {
return false
}
// IsShared checks that the label includes a "shared" mark
func IsShared(label string) bool {
return false
@@ -181,6 +181,11 @@ func Validate(label string) error {
return nil
}
// RelabelNeeded checks whether the user requested a relabel
func RelabelNeeded(label string) bool {
return strings.Contains(label, "z") || strings.Contains(label, "Z")
}
// IsShared checks that the label includes a "shared" mark
func IsShared(label string) bool {
return strings.Contains(label, "z")